18

我正在阅读核心 C# 编程结构,并且很难将我的头脑围绕在out参数修饰符上。我通过阅读知道它的作用,但是当我使用它时,我试图想出一个场景。

有人可以给我一个真实的例子吗?谢谢。

4

8 回答 8

19

使用out参数的主要动机是允许函数向调用者返回多个值,并且其他所有人都在框架中提供了示例。我将out首先通过探索参数背后的推理来采用不同的方法来回答您的问题。我不会写出实际的例子,而是描述它们。

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

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

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

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

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

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

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

于 2010-11-05T02:40:09.467 回答
17

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

string str = "123456";
int val;
if ( !int.TryParse(str,out val) )
{
// do some error handling, notify user, etc.
}
于 2010-11-05T01:32:30.593 回答
6

当然,看看任何一种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),它获取锁以原子方式设置a bool,这不可能仅通过返回值来实现,因为锁获取必然发生在将返回值分配给bool变量之前。ref(是的,在技术上out并不相同;但它们非常接近)。

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

于 2010-11-05T01:34:00.387 回答
4

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

于 2010-11-05T01:34:37.117 回答
2

很简单,当您有一个返回多个值的方法时。最“著名”的案例之一是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.");
}
于 2010-11-05T01:34:02.947 回答
1

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

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 参数。

于 2010-11-05T05:22:03.663 回答
0
//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;
}
于 2014-08-28T08:01:37.867 回答
0
bool Int32.TryParse(String, out Int);

或类似 Dictionary.TryGetValue 的东西。

但是我认为使用它不是一个很好的做法,当然,使用 API 提供的那些,比如 Int32 来避免 Try-Catch 是例外。

于 2010-11-05T01:34:44.360 回答