在c#中,当向方法发送参数时,我们何时应该使用“ref”和何时“out”以及何时没有任何参数?
答案 0 :(得分:13)
一般情况下,如果可能,应避免使用ref和out。
话虽如此,当方法可能需要来修改值时,请使用ref。当方法始终为值赋值时,请使用。
ref和out之间的区别在于,当使用时,编译器会强制执行规则,您需要在返回之前为out参数指定一些内容。使用ref时,必须在之前为变量赋值,并将其用作ref参数。
显然,当您编写自己的方法时,上述情况适用。如果需要调用在参数上使用ref或out修饰符声明的方法,则在调用方法时,应在参数之前使用相同的修饰符。
还要记住,C#通过引用传递引用类型(类)(如,引用按值传递)。因此,如果您提供一些引用类型作为参数的方法,该方法可以修改对象的数据;即使没有引用或退出。但是它不能修改引用本身(因为它不能修改引用的对象)。
答案 1 :(得分:12)
它们主要用于从方法调用中获取多个返回值。就个人而言,我倾向于不使用它们。如果我想要一个方法的多个返回值,那么我将创建一个小类来保存它们。
当您想要从该参数中的方法返回某些内容时,将使用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之前的日子。 (但是,这只是我的观点)
答案 2 :(得分:6)
(这是现有答案的补充 - 一些额外的考虑因素)
在C#中使用ref
还有另一种情况,在XNA之类的东西中更常见......通常,当您传递值类型(struct
)时,它会被克隆。这使用堆栈空间和几个CPU周期,并且具有副作用,即对调用方法中的struct
的任何修改都将丢失。
(除了:通常struct
s应该是不可变的,但XNA中的可变结构并不罕见)
为了解决这个问题,在这些程序中看到ref
是很常见的。
但是在大多数程序中(例如,您使用class
作为默认值),您可以通常只传递“按值”引用(即没有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
的人也能理解它。
答案 3 :(得分:2)
非常简单。您使用与方法中最初声明参数的关键字完全相同的关键字。如果它被声明为out
,则必须使用out
。如果它被声明为ref
,则必须使用ref
。
答案 4 :(得分:2)
除了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(); }
只需确保为每个out参数指定一个有效值,以避免出错。
答案 5 :(得分:0)
尽量避免使用ref。 Out是可以的,因为你知道将会发生什么,旧值将消失,即使函数失败,新值也将在你的变量中。但是,只要查看函数,您就不知道ref参数会发生什么。它可以是相同的,修改的或全新的对象。
每当我看到裁判时,我都会感到紧张。
答案 6 :(得分:0)
ref是要避免的(我相信这也有一个fx-cop规则)但是当引用的对象本身可能改变时使用ref。如果您看到'ref'关键字,则您知道在调用该方法后,基础对象可能不再被同一个变量引用。