我正在阅读核心 C# 编程结构,并且很难将我的头脑围绕在out
参数修饰符上。我通过阅读知道它的作用,但是当我使用它时,我试图想出一个场景。
有人可以给我一个真实的例子吗?谢谢。
我正在阅读核心 C# 编程结构,并且很难将我的头脑围绕在out
参数修饰符上。我通过阅读知道它的作用,但是当我使用它时,我试图想出一个场景。
有人可以给我一个真实的例子吗?谢谢。
使用out
参数的主要动机是允许函数向调用者返回多个值,并且其他所有人都在框架中提供了示例。我将out
首先通过探索参数背后的推理来采用不同的方法来回答您的问题。我不会写出实际的例子,而是描述它们。
通常,您只有一种返回值的机制,即函数的返回值。当然,您也可以使用全局(静态)或实例变量,但这通常不太实用也不安全(原因我不会在这里解释)。 在 .NET 3.5 之前,没有一种真正实用的方法可以从函数返回多个值。如果out
orref
修饰符不可用,您将有几个选项:
如果所有值都具有相同的类型,则可以返回一些值的集合。这在大多数情况下都很好,您可以返回一个数字数组、字符串列表等等。如果所有值都以完全相同的方式相关,这是完美的。即,所有数字都是容器中的物品数量,或者列表是聚会中客人的姓名。但是,如果您返回的值代表不同的数量怎么办?如果他们有不同的类型呢?对象列表可以包含所有对象,但这不是操作此类数据的一种非常直观的方式。
对于需要返回多个不同类型的值的情况,唯一可行的选择是创建一个新的类/结构类型来封装所有这些值并返回该类型的实例。这样做可以返回具有直观名称的强类型值,并且可以通过这种方式返回多个值。问题是,为了获得它,您必须使用特定名称和所有内容定义类型,以便能够返回多个值。如果您只想返回两个足够简单的值而无法为其创建类型怎么办?您还有更多选择:
您可以创建一组泛型类型来包含固定数量的不同类型的值(如函数式语言中的元组)。但是以可重用的方式这样做并没有那么吸引人,因为它当时不是框架的一部分。它可以放在一个库中,但现在您只是为了这些简单类型而添加对该库的依赖。(很高兴 .NET 4.0 现在包含该Tuple
类型)但这仍然不能解决这些是简单值的事实,这意味着增加了简单任务的复杂性。
使用的选项是包含一个out
修饰符,该修饰符允许调用者将“引用”传递给变量,以便函数可以将引用的变量设置为另一种返回值的方式。出于同样的原因,这种返回值的方式在 C 和 C++ 中也以多种方式可用,并在影响这一决定方面发挥了作用。然而,C# 的不同之处在于,对于out
参数,函数必须将值设置为某个值。如果没有,则会导致编译器错误。这使得这不太容易出错,因为通过使用out
参数,您向调用者承诺您将设置该值并且他们可以使用它,编译器确保您遵守该承诺。
out
关于(or ) 修饰符的典型用法的注释,ref
很少会看到超过一个或两个out
参数。在这些情况下,创建封装类型几乎总是一个更好的主意。如果您只需要返回一个值,您通常会使用它。
然而,由于 C#-3.0/.NET-3.5 引入了 .NET 4.0 中引入的匿名类型和元组,这些选项提供了替代方法,可以更轻松(也更直观)地返回多个不同类型的值。
有很多场景可以使用它,但主要的场景是您的方法需要返回一个以上的参数。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.
}
当然,看看任何一种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)
。
很简单,当您有一个返回多个值的方法时。最“著名”的案例之一是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.");
}
正如其他人所说 - 输出参数允许我们从方法调用中返回多个值,而不必将结果包装在结构/类中。
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 参数。
//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;
}
bool Int32.TryParse(String, out Int);
或类似 Dictionary.TryGetValue 的东西。
但是我认为使用它不是一个很好的做法,当然,使用 API 提供的那些,比如 Int32 来避免 Try-Catch 是例外。