【发布时间】:2010-11-10 02:06:34
【问题描述】:
在c#中,当向方法发送参数时,我们应该什么时候使用“ref”,什么时候使用“out”,什么时候不使用它们?
【问题讨论】:
标签: c# methods parameters
在c#中,当向方法发送参数时,我们应该什么时候使用“ref”,什么时候使用“out”,什么时候不使用它们?
【问题讨论】:
标签: c# methods parameters
真的很简单。您使用与方法中最初声明参数的关键字完全相同的关键字。如果它被声明为out,则必须使用out。如果它被声明为ref,则必须使用ref。
【讨论】:
一般来说,如果可能,您应该避免使用 ref 和 out。
话虽如此,当方法可能需要修改值时使用ref。当方法总是应该为值赋值时使用 out。
ref 和 out 的区别在于,在使用 out 时,编译器会强制执行规则,即您需要在返回之前为 out 参数赋值。使用 ref 时,必须在将变量用作 ref 参数之前为变量赋值。
显然,当您编写自己的方法时,上述内容适用。如果您需要调用在其参数上使用 ref 或 out 修饰符声明的方法,则在调用方法时应在参数前使用相同的修饰符。
还请记住,C# 通过引用传递引用类型(类)(如引用按值传递)。所以如果给某个方法提供引用类型作为参数,该方法可以修改对象的数据;即使没有 ref 或 out。但它不能修改引用本身(例如,它不能修改被引用的对象)。
【讨论】:
它们主要用于从方法调用中获取多个返回值。就个人而言,我倾向于不使用它们。如果我想要一个方法的多个返回值,那么我将创建一个小类来保存它们。
ref 和 out 用于当您希望从该参数中的方法返回某些内容时。我记得,它们实际上都编译为相同的 IL,但 C# 放置了一些额外的东西,所以你必须具体。
这里有一些例子:
static void Main(string[] args)
{
string myString;
MyMethod0(myString);
Console.WriteLine(myString);
Console.ReadLine();
}
public static void MyMethod0(string param1)
{
param1 = "Hello";
}
上面的代码不会编译,因为 myString 从未初始化。如果 myString 被初始化为 string.Empty,那么程序的输出将是一个空行,因为 MyMethod0 所做的只是将一个新字符串分配给对 param1 的本地引用。
static void Main(string[] args)
{
string myString;
MyMethod1(out myString);
Console.WriteLine(myString);
Console.ReadLine();
}
public static void MyMethod1(out string param1)
{
param1 = "Hello";
}
myString 未在 Main 方法中初始化,但程序输出“Hello”。这是因为 Main 方法中的 myString 引用是从 MyMethod1 更新的。 MyMethod1 不希望 param1 已经包含任何内容,因此它可以保持未初始化。但是,该方法应该分配一些东西。
static void Main(string[] args)
{
string myString;
MyMethod2(ref myString);
Console.WriteLine(myString);
Console.ReadLine();
}
public static void MyMethod2(ref string param1)
{
param1 = "Hello";
}
这同样不会编译。这是因为 ref 要求 Main 方法中的 myString 首先初始化为某个值。但是,如果 Main 方法被更改,以便 myString 被初始化为 string.Empty,那么代码将编译并且输出将为 Hello。
因此,区别在于可以与未初始化的对象一起使用, ref 必须传递一个已初始化的对象。如果你传递一个没有任何引用的对象,它的引用就不能被替换。
要明确一点:如果传递的对象已经是引用类型,则该方法可以更新对象,并且更新会反映在调用代码中,但是对对象的引用不能更改。所以如果我写这样的代码:
static void Main(string[] args)
{
string myString = "Hello";
MyMethod0(myString);
Console.WriteLine(myString);
Console.ReadLine();
}
public static void MyMethod0(string param1)
{
param1 = "World";
}
程序的输出将是 Hello,而不是 World,因为该方法只更改了引用的本地副本,而不是传入的引用。
我希望这是有道理的。我的一般经验法则就是不要使用它们。我觉得这是回到 OO 之前的日子。 (不过,这只是我的看法)
【讨论】:
(这是对现有答案的补充 - 一些额外的考虑)
在 C# 中使用 ref 还有另一种情况,在 XNA 等中更常见...通常,当您传递值类型 (struct) 时,它会被克隆。这会使用堆栈空间和一些 CPU 周期,并具有副作用,即在调用的方法中对 struct 的任何修改都会丢失。
(另外:通常structs 应该是不可变的,但可变结构在 XNA 中并不少见)
为了解决这个问题,在此类程序中看到ref 是很常见的。
但在大多数程序中(即默认使用classes),通常只需“按值”传递引用(即不ref/out)。
out 的另一个非常常见用例是Try* 模式,例如:
string s = Console.ReadLine();
int i;
if(int.TryParse(s, out i)) {
Console.WriteLine("You entered a valid int: " + i);
}
或者类似地,TryGetValue 在字典上。
这可以使用元组代替,但它是一种常见的模式,即使是那些与ref/out 斗争太多的人也可以合理理解。
【讨论】:
尽量避免使用 ref. Out 没关系,因为你知道会发生什么,即使函数失败,旧值也会消失,新值会出现在你的变量中。但是,仅通过查看函数,您不知道 ref 参数会发生什么。它可能是相同的、修改的或全新的对象。
每当我看到 ref 时,我都会紧张。
【讨论】:
ref 是要避免的(我相信这也有一个 fx-cop 规则)但是当引用的对象本身可能发生变化时使用 ref 。如果您看到 'ref' 关键字,您就知道在调用方法后,底层对象可能不再被同一个变量引用。
【讨论】:
除了 Colin 的详细回答,您还可以使用 out 参数从一个方法调用中返回多个值。参见下面的方法,它返回 3 个值。
static void AssignSomeValues(out int first, out bool second, out string third)
{
first = 12 + 12;
second = false;
third = "Output parameters are okay";
}
你可以这样使用它
static void Main(string[] args) {
int i;
string s;
bool b;
AssignSomeValues(out i, out b, out s);
Console.WriteLine("Int value: {0}", i);
Console.WriteLine("Bool value: {0}", b);
Console.WriteLine("String value: {0}", s);
//wait for enter key to terminate program
Console.ReadLine(); }
只需确保为每个输出参数分配一个有效值即可避免出错。
【讨论】: