C#“出局”的真实示例参数有用吗?

发布于 2024-09-30 13:42:57 字数 108 浏览 6 评论 0原文

我正在阅读核心 C# 编程结构,但很难理解 out 参数修饰符。我通过阅读知道它的作用,但我试图想出当我使用它时的场景。

有人能给我一个现实世界的例子吗?谢谢。

I'm reading up on core C# programming constructs and having a hard time wrapping my head around the out parameter modifier. I know what it does by reading but am trying to think of a scenerio when I would use it.

Can someone give me a real-world example? Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

柠檬色的秋千 2024-10-07 13:42:57

使用 out 参数的主要动机是允许函数将多个值返回给调用者以及框架中其他人提供的示例。我将采用不同的方法来回答您的问题,首先探讨使用 out 参数背后的原因。我不会写出实际的例子,而是描述它们。

通常,只有一种返回值的机制,即函数的返回值。当然,您也可以使用全局(静态)或实例变量,但这通常不太实用也不安全(出于我不会在这里解释的原因)。 在 .NET 3.5 之前,没有一种真正实用的方法可以从函数返回多个值。如果 outref 修饰符不可用,您将有几个选择:

  • 如果所有值都具有相同的类型,您可以返回一些值的集合。在大多数情况下,这完全没问题,您可以返回数字数组、字符串列表等。如果所有值都以完全相同的方式相关,那就完美了。即,所有数字都是容器中物品的数量,或者列表是聚会上客人的姓名。但是如果您返回的值代表不同的数量怎么办?如果它们有不同的类型怎么办?对象列表可以容纳所有对象,但这不是操作此类数据的非常直观的方式。

  • 对于需要返回不同类型的多个值的情况,唯一实用的选择是创建一个新的类/结构类型来封装所有这些值并返回该类型的实例。这样做可以返回具有直观名称的强类型值,并且可以通过这种方式返回多个值。问题是,为了实现这一点,您必须使用特定名称和所有内容来定义类型,以便能够返回多个值。如果您只想返回两个值,而这两个值足够简单,以至于无法为其创建类型,该怎么办?您又多了几个选择:

    • 您可以创建一组泛型类型来包含固定数量的不同类型的值(如函数语言中的元组)。但以可重用的方式这样做并不那么有吸引力,因为它当时不是框架的一部分。它可以放入一个库中,但现在您只是为了这些简单类型而添加对该库的依赖项。 (很高兴 .NET 4.0 现在包含 Tuple 类型)但这仍然不能解决这些简单值的事实,这意味着简单任务会增加复杂性。

      李>

    • 使用的选项是包含一个 out 修饰符,它允许调用者将“引用”传递给变量,以便函数可以将引用的变量设置为另一种方式返回一个值。出于同样的原因,这种返回值的方式在 C 和 C++ 中也以多种方式可用,并且在影响这一决定方面发挥了作用。然而,C# 中的区别在于,对于 out 参数,函数必须将值设置为某个值。如果不存在,则会导致编译器错误。这使得这种情况不太容易出错,因为通过使用 out 参数,您向调用者承诺您会将值设置为某个值并且他们可以使用它,编译器会确保您遵守该承诺。

关于 out(或 ref)修饰符的典型用法的注释,很少会看到超过一两个 out 参数。在这些情况下,创建封装类型几乎总是一个更好的主意。如果您只需要再返回一个值,您通常会使用它。

然而,自从 C#-3.0/.NET-3.5 引入了 .NET 4.0 中引入的匿名类型和元组以来,这些选项提供了替代方法来更轻松(且更直观)地返回不同类型的多个值。

The main motivation to using an out parameter is to allow a function to return multiple values to the caller and everyone else provided examples in the framework. I'll take a different approach to answering your question by exploring the reasoning behind having out parameters in the first place. I won't write out actual examples but describe them.

Normally you have only one mechanism to return values, the function's return value. Sure you could use a global (static) or instance variables too but that's not very practical nor safe to do in general (for reasons I won't explain here). Prior to .NET 3.5, there wasn't a really practical way to return multiple values from a function. If out or ref modifiers were not available, you would have a few options:

  • If all your values had the same type, you could return some collection of the values. This is perfectly fine in most cases, you could return an array of number, list of strings, whatever. This is perfect if all the values were related in exactly the same way. i.e., All numbers were the number of items in a container, or the list was of names of guests at a party. But what if the values you returned represented different quantities? What if they had different types? A list of objects could hold them all but it is not a very intuitive way to manipulate that sort of data.

  • For the case when you need to return multiple values of different types, the only practical option you had was to create a new class/struct type to encapsulate all these values and return an instance of that type. Doing so you could return strongly typed values with intuitive names and you could return multiple values this way. The problem is that in order to get that, you had to define the type with a specific name and everything just to be able to return multiple values. What if you wanted to return only two values which were simple enough making it impractical to create a type for it? You have a couple more options again:

    • You could create a set of generic types to contain a fixed amount of values of varying types (like a tuple in functional languages). But it is not as appealing to do so in a reusable manner since it wasn't part of the framework at the time. It could be put in a library but now you add a dependency on that library just for the sake of these simple types. (just be glad that .NET 4.0 now includes the Tuple type) But this still doesn't solve the fact that these are simple values which means added complexity for a simple task.

    • The option that was used was to include an out modifier which allows the caller to pass a "reference" to a variable so that the function may set the referenced variable as another way to return a value. This way of returning values was also available in C and C++ in many ways for the same reasons and played a role in influencing this decision. However the difference in C# is that for an out parameter, the function must set the value to something. If it doesn't, it results in a compiler error. This makes this less error prone since by having an out parameter, you're promising the caller that you will set the value to something and they can use it, the compiler makes sure you stick to that promise.

A note on the typical usage of the out (or ref) modifier, it will be rare to see more than one or two out parameters. In those cases, it will almost always be a much better idea to create the encapsulating type. You would typically use it if you needed just one more value to return.

However since C#-3.0/.NET-3.5 with the introduction of anonymous types and tuples introduced in .NET 4.0, these options provided alternative methods to return multiple values of varying types easier (and more intuitive) to do.

偏爱你一生 2024-10-07 13:42:57

您会在很多场景中使用它,但主要的场景是您的方法需要返回多个参数。以 int 类型的 TryParse 方法为例。在这种情况下,不是抛出异常,而是返回 bool 作为成功/失败标志,并且解析的 int 作为输出参数返回。如果您要调用 int.Parse(...) 您可能会抛出异常。

string str = "123456";
int val;
if ( !int.TryParse(str,out val) )
{
// do some error handling, notify user, etc.
}

there are many scenarios where you would use it, but the main one would be where your method needs to return more then one parameter. Take, for example, the TryParse methods on int type. In this case, instead of throwing an exception a bool is returned as a success/failure flag and the parsed int is return as the out param. if you were to call int.Parse(...) you could potentially throw an exception.

string str = "123456";
int val;
if ( !int.TryParse(str,out val) )
{
// do some error handling, notify user, etc.
}
硬不硬你别怂 2024-10-07 13:42:57

当然,请查看任何 TryParse 方法,例如 int.TryParse

这个想法是你实际上想要两条条信息:解析操作是否成功(返回值), ,如果是这样,它的实际结果是什么(out 参数)。

用法:

string input = Console.ReadLine();
int value;

// First we check the return value, which is a bool
// indicating success or failure.
if (int.TryParse(input, out value))
{
    // On success, we also use the value that was parsed.
    Console.WriteLine(
        "You entered the number {0}, which is {1}.",
        value,
        value % 2 == 0 ? "even" : "odd"
    );
}
else
{
    // Generally, on failure, the value of an out parameter
    // will simply be the default value for the parameter's
    // type (e.g., default(int) == 0). In this scenario you
    // aren't expected to use it.
    Console.WriteLine(
        "You entered '{0}', which is not a valid integer.",
        input
    );
}

许多开发人员抱怨 out 参数是“代码味道”;但在许多情况下它们可能是迄今为止最合适的选择。一个非常重要的现代例子是多线程代码;通常,需要 out 参数来允许返回值不足以实现的“原子”操作。

例如,考虑一下 Monitor.TryEnter(object, ref bool),它获取一个锁原子地设置一个 bool,这是不可能的可以仅通过返回值来实现,因为锁获取必然发生在将返回值分配给 bool 变量之前。 (是的,从技术上讲,refout 并不相同;但它们非常接近)。

另一个很好的例子是 .NET 4.0 新增的 System.Collections.Concurrent 命名空间中的集合类可用的一些方法;它们提供类似的线程安全操作,例如 ConcurrentQueue.TryDequeue(out T) 和 ConcurrentDictionary.TryRemove(TKey, out TValue)。

Sure, take a look at any of the TryParse methods, such as int.TryParse:

The idea is you actually want two pieces of information: whether a parse operation was successful (the return value), and, if so, what the result of it actually was (the out parameter).

Usage:

string input = Console.ReadLine();
int value;

// First we check the return value, which is a bool
// indicating success or failure.
if (int.TryParse(input, out value))
{
    // On success, we also use the value that was parsed.
    Console.WriteLine(
        "You entered the number {0}, which is {1}.",
        value,
        value % 2 == 0 ? "even" : "odd"
    );
}
else
{
    // Generally, on failure, the value of an out parameter
    // will simply be the default value for the parameter's
    // type (e.g., default(int) == 0). In this scenario you
    // aren't expected to use it.
    Console.WriteLine(
        "You entered '{0}', which is not a valid integer.",
        input
    );
}

Many developers complain of out parameters as a "code smell"; but they can be by far the most appropriate choice in many scenarios. One very important modern example would be multithreaded code; often an out parameter is necessary to permit "atomic" operations where a return value would not suffice.

Consider for example Monitor.TryEnter(object, ref bool), which acquires a lock and sets a bool atomically, something that wouldn't be possible via a return value alone since the lock acquisition would necessarily happen before the return value were assigned to a bool variable. (Yes, technically ref and out are not the same; but they're very close).

Another good example would be some of the methods available to the collection classes in the System.Collections.Concurrent namespace new to .NET 4.0; these provide similarly thread-safe operations such as ConcurrentQueue<T>.TryDequeue(out T) and ConcurrentDictionary<TKey, TValue>.TryRemove(TKey, out TValue).

不寐倦长更 2024-10-07 13:42:57

输出参数遍布整个 .NET 框架。我最常看到的一些用法是 TryParse 方法,它返回一个布尔值(指示解析是否有效),实际结果通过输出参数返回。虽然当您需要返回多个值时使用类也是很常见的地方,但在这样的示例中,它有点严厉。有关输出参数的更多信息,请参阅 Jon Skeet 的文章:C# 中的参数传递

Output parameters are found all over the .NET framework. Some of the uses I see most often are the TryParse methods, which return a boolean (indicating whether or not the parse was valid) and the actual result is returned via the output parameter. While it's also very common place to use a class when you need to return multiple values, in such an example as this it's a little heavy handed. For more on output parameters, see Jon Skeet's article on Parameter passing in C#.

锦爱 2024-10-07 13:42:57

很简单,当您有一种返回多个值的方法时。
最“著名”的案例之一是 Dictionary.TryGetValue

string value = "";

if (openWith.TryGetValue("tif", out value))
{
    Console.WriteLine("For key = \"tif\", value = {0}.", value);
}
else
{
    Console.WriteLine("Key = \"tif\" is not found.");
}

Simple, when you have a method that returns more than one value.
One of the most "famous" cases is Dictionary.TryGetValue:

string value = "";

if (openWith.TryGetValue("tif", out value))
{
    Console.WriteLine("For key = \"tif\", value = {0}.", value);
}
else
{
    Console.WriteLine("Key = \"tif\" is not found.");
}
萌面超妹 2024-10-07 13:42:57

正如其他人所说 - 输出参数允许我们从方法调用中返回多个值,而不必将结果包装在结构/类中。

xxx.TryParse 方法的添加极大地简化了字符串值(通常来自 UI)和原始类型之间转换所需的编码。

您可能必须编写才能实现相同功能的示例如下:

/// <summary>
/// Example code for how <see cref="int.TryParse(string,out int)"/> might be implemented.
/// </summary>
/// <param name="integerString">A string to convert to an integer.</param>
/// <param name="result">The result of the parse if the operation was successful.</param>
/// <returns>true if the <paramref name="integerString"/> parameter was successfully 
/// parsed into the <paramref name="result"/> integer; false otherwise.</returns>
public bool TryParse(string integerString, out int result)
{
    try
    {
        result = int.Parse(integerString);
        return true;
    }
    catch (OverflowException)
    {
        // Handle a number that was correctly formatted but 
        // too large to fit into an Int32.
    }
    catch (FormatException)
    {
        // Handle a number that was incorrectly formatted 
        // and so could not be converted to an Int32.
    }

    result = 0; // Default.
    return false;
}

此处避免的两个异常检查使调用代码更具可读性。我相信实际的 .NET 实现完全避免了异常,因此性能也更好。同样,此示例显示了 IDictionary.TryGetValue(...) 如何使代码更简单、更高效:

private readonly IDictionary<string,int> mDictionary = new Dictionary<string, int>();

public void IncrementCounter(string counterKey)
{
    if(mDictionary.ContainsKey(counterKey))
    {
        int existingCount = mDictionary[counterKey];

        mDictionary[counterKey] = existingCount + 1;
    }
    else
    {
        mDictionary.Add(counterKey, 1);
    }
}

public void TryIncrementCounter(string counterKey)
{
    int existingCount;
    if (mDictionary.TryGetValue(counterKey, out existingCount))
    {
        mDictionary[counterKey] = existingCount + 1;
    }
    else
    {
        mDictionary.Add(counterKey, 1);
    }
}

这一切都归功于 out 参数。

As others have said - out parameters allow us to return more than one value from a method call without having to wrap the results in struct/class.

The addition of the xxx.TryParse methods greatly simplified the coding necessary to convert between a string value (frequently from the UI) and a primitive type.

An example of what you might have had to write to achieve the same functionality is here:

/// <summary>
/// Example code for how <see cref="int.TryParse(string,out int)"/> might be implemented.
/// </summary>
/// <param name="integerString">A string to convert to an integer.</param>
/// <param name="result">The result of the parse if the operation was successful.</param>
/// <returns>true if the <paramref name="integerString"/> parameter was successfully 
/// parsed into the <paramref name="result"/> integer; false otherwise.</returns>
public bool TryParse(string integerString, out int result)
{
    try
    {
        result = int.Parse(integerString);
        return true;
    }
    catch (OverflowException)
    {
        // Handle a number that was correctly formatted but 
        // too large to fit into an Int32.
    }
    catch (FormatException)
    {
        // Handle a number that was incorrectly formatted 
        // and so could not be converted to an Int32.
    }

    result = 0; // Default.
    return false;
}

The two exception checks that are avoided here make the calling code much more readable. I believe that the actual .NET implementations avoid the exceptions altogether so perform better as well. Similarly, this example shows how IDictionary.TryGetValue(...) makes code simpler and more efficient:

private readonly IDictionary<string,int> mDictionary = new Dictionary<string, int>();

public void IncrementCounter(string counterKey)
{
    if(mDictionary.ContainsKey(counterKey))
    {
        int existingCount = mDictionary[counterKey];

        mDictionary[counterKey] = existingCount + 1;
    }
    else
    {
        mDictionary.Add(counterKey, 1);
    }
}

public void TryIncrementCounter(string counterKey)
{
    int existingCount;
    if (mDictionary.TryGetValue(counterKey, out existingCount))
    {
        mDictionary[counterKey] = existingCount + 1;
    }
    else
    {
        mDictionary.Add(counterKey, 1);
    }
}

And all thanks to the out parameter.

街角卖回忆 2024-10-07 13:42:57
bool Int32.TryParse(String, out Int);

或类似的东西,如Dictionary.TryGetValue。

但我认为这并不是一种很好的做法,当然,使用 Int32 等 API 提供的 API 来避免 Try-Catch 是例外。

bool Int32.TryParse(String, out Int);

or something similar like Dictionary.TryGetValue.

But I would consider this one to be a not too good practice to employ it, of course, using those provided by API like the Int32 one to avoid Try-Catch is exceptions.

煮茶煮酒煮时光 2024-10-07 13:42:57
//out key word is used in function instead of return. we can use multiple parameters by using out key word
public void outKeyword(out string Firstname, out string SecondName)
{
    Firstname = "Muhammad";
    SecondName = "Ismail";

}
//on button click Event
protected void btnOutKeyword_Click(object sender, EventArgs e)
{
    string first, second;
    outKeyword(out first, out second);
    lblOutKeyword.Text = first + "  " + second;
}
//out key word is used in function instead of return. we can use multiple parameters by using out key word
public void outKeyword(out string Firstname, out string SecondName)
{
    Firstname = "Muhammad";
    SecondName = "Ismail";

}
//on button click Event
protected void btnOutKeyword_Click(object sender, EventArgs e)
{
    string first, second;
    outKeyword(out first, out second);
    lblOutKeyword.Text = first + "  " + second;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文