前几天有人问我他们什么时候应该使用参数关键字out而不是ref。虽然我(我认为)理解ref和out关键字之间的区别(之前已经问过了),最好的解释似乎是ref == in和out,有什么(假设或代码)的例子,我应该总是使用out而不是ref。
既然ref更通用,为什么还要用out呢?它只是语法上的糖吗?
前几天有人问我他们什么时候应该使用参数关键字out而不是ref。虽然我(我认为)理解ref和out关键字之间的区别(之前已经问过了),最好的解释似乎是ref == in和out,有什么(假设或代码)的例子,我应该总是使用out而不是ref。
既然ref更通用,为什么还要用out呢?它只是语法上的糖吗?
当前回答
只是在OP的注释中澄清一下,对ref和out的使用是“对在方法外部声明的值类型或结构的引用”,这已经在incorrect中建立了。
考虑在StringBuilder上使用ref,它是一种引用类型:
private void Nullify(StringBuilder sb, string message)
{
sb.Append(message);
sb = null;
}
// -- snip --
StringBuilder sb = new StringBuilder();
string message = "Hi Guy";
Nullify(sb, message);
System.Console.WriteLine(sb.ToString());
// Output
// Hi Guy
与此相反:
private void Nullify(ref StringBuilder sb, string message)
{
sb.Append(message);
sb = null;
}
// -- snip --
StringBuilder sb = new StringBuilder();
string message = "Hi Guy";
Nullify(ref sb, message);
System.Console.WriteLine(sb.ToString());
// Output
// NullReferenceException
其他回答
需要注意的是,in在c# ver 7.2是一个有效的关键字:
The in parameter modifier is available in C# 7.2 and later. Previous versions generate compiler error CS8107 ("Feature 'readonly references' is not available in C# 7.0. Please use language version 7.2 or greater.") To configure the compiler language version, see Select the C# language version. ... The in keyword causes arguments to be passed by reference. It makes the formal parameter an alias for the argument, which must be a variable. In other words, any operation on the parameter is made on the argument. It is like the ref or out keywords, except that in arguments cannot be modified by the called method. Whereas ref arguments may be modified, out arguments must be modified by the called method, and those modifications are observable in the calling context.
如果计划读取和写入参数,则需要使用ref。如果你只打算写,你需要使用out。实际上,当您需要多个返回值时,或者当您不想使用正常的返回机制进行输出时(但这种情况应该很少发生),就会使用out。
有一些语言机制可以帮助这些用例。Ref形参在传递给方法之前必须已经初始化(强调它们是可读可写的),out形参在被赋值之前不能被读取,并且保证在方法结束时已经被写入(强调它们只能被写入)。违反这些原则将导致编译时错误。
int x;
Foo(ref x); // error: x is uninitialized
void Bar(out int x) {} // error: x was not written to
例如,int。TryParse返回bool类型,并接受out int形参:
int value;
if (int.TryParse(numericString, out value))
{
/* numericString was parsed into value, now do stuff */
}
else
{
/* numericString couldn't be parsed */
}
这是需要输出两个值的一个明确示例:数值结果和转换是否成功。CLR的作者决定在这里选择out,因为他们不关心int之前可能是什么。
作为参考,你可以看联锁。增量:
int x = 4;
Interlocked.Increment(ref x);
联锁。Increment自动地增加x的值。因为需要读取x来增加它,所以在这种情况下ref更合适。你完全关心x在传递给Increment之前是什么。
在c#的下一个版本中,甚至可以声明变量In out形参,更加强调它们只输出的性质:
if (int.TryParse(numericString, out int value))
{
// 'value' exists and was declared in the `if` statement
}
else
{
// conversion didn't work, 'value' doesn't exist here
}
下面是我从这篇关于c# Out Vs Ref的codeproject文章中摘录的一些注释
只有当我们期望从一个函数或方法得到多个输出时,才应该使用它。对结构的思考也是一个不错的选择。 REF和OUT是关键字,它们指示数据如何从调用方传递到被调用方,反之亦然。 在REF中,数据以两种方式传递。从呼叫者到被呼叫者,反之亦然。 In Out数据仅以一种方式从被调用方传递给调用方。在这种情况下,如果Caller试图向被调用方发送数据,它将被忽略/拒绝。
如果你是一个视觉人,那么请看看这个yourtube视频,它演示了实际的区别https://www.youtube.com/watch?v=lYdcY5zulXA
下图更直观地显示了差异
你应该使用out,除非你需要参考。
当数据需要被编组到另一个进程时,它会产生很大的不同,这可能会花费很高。因此,您希望避免在方法不使用初始值时编组初始值。
除此之外,它还向声明或调用的读者显示初始值是相关的(并可能被保留),还是被丢弃。
作为一个微小的区别,out形参不需要初始化。
out的示例:
string a, b;
person.GetBothNames(out a, out b);
其中GetBothNames是一个原子检索两个值的方法,无论a和b是什么,该方法都不会改变行为。如果调用到夏威夷的服务器,将初始值从这里复制到夏威夷是浪费带宽。使用ref的类似代码段:
string a = String.Empty, b = String.Empty;
person.GetBothNames(ref a, ref b);
可能会让读者感到困惑,因为看起来a和b的初始值是相关的(尽管方法名表明它们不是)。
引用的例子:
string name = textbox.Text;
bool didModify = validator.SuggestValidName(ref name);
这里的初始值与方法相关。
在c#中如何使用in或out或ref ?
All keywords in C# have the same functionality but with some boundaries. in arguments cannot be modified by the called method. ref arguments may be modified. ref must be initialized before being used by caller it can be read and updated in the method. out arguments must be modified by the caller. out arguments must be initialized in the method Variables passed as in arguments must be initialized before being passed in a method call. However, the called method may not assign a value or modify the argument.
以下类型的方法不能使用in、ref和out关键字:
Async方法,通过使用Async修饰符来定义。 迭代器方法,其中包括yield return或yield break语句。