我在生产代码中越多地看到 ref 的使用,就越遇到滥用和带来的痛苦。我开始厌恶这个关键字了,因为从框架构建的角度来看,它似乎很愚蠢。在什么情况下将对象引用/值的改变可能性传达给代码使用者是一个好主意呢?
相比之下,我喜欢 out 关键字,更喜欢根本不使用任何关键字,因为使用它们时可以得到保证。然而,ref 并没有提供任何保证,除了强制在传递参数之前对其进行初始化之外,即使可能不会对其进行任何更改。
虽然我不是一个睿智的开发人员;我肯定它有实际应用的用途,但我想知道它们是什么。
我在生产代码中越多地看到 ref 的使用,就越遇到滥用和带来的痛苦。我开始厌恶这个关键字了,因为从框架构建的角度来看,它似乎很愚蠢。在什么情况下将对象引用/值的改变可能性传达给代码使用者是一个好主意呢?
相比之下,我喜欢 out 关键字,更喜欢根本不使用任何关键字,因为使用它们时可以得到保证。然而,ref 并没有提供任何保证,除了强制在传递参数之前对其进行初始化之外,即使可能不会对其进行任何更改。
虽然我不是一个睿智的开发人员;我肯定它有实际应用的用途,但我想知道它们是什么。
ref
和out
参数。
《框架设计指南》列举了规范的避免使用
out
或ref
参数。使用
out
或ref
参数需要对指针有经验,理解值类型和引用类型的区别,并处理具有多个返回值的方法。此外,out
和ref
参数之间的区别并不被广泛理解。为了适应普通用户,框架架构师在设计时不应该期望用户掌握使用out
或ref
参数的技能。
Swap
方法作为有效例外:void Swap<T>(ref T obj1, ref T obj2)
{
T temp = obj1;
obj1 = obj2;
obj2 = temp;
}
但同时有一条评论指出:
在这些讨论中,交换(swap)总是被提及,但自从大学以来我就没有写过需要使用交换方法的代码了。除非你有非常好的理由,否则应完全避免使用
out
和ref
。
我尽量避免在公共API中使用它,但它确实有用途。可变值类型是其中一个重要应用场景,尤其是在类似CF(由于平台要求使用可变结构体更为普遍)的东西上。然而,我最常使用它的时候是将复杂算法的部分重构到几个方法中时,其中状态对象过于笨重,需要传递多个值:
例如:
var x = .....
var y = .....
// some local code...
var z = DoSomethingSpecific(ref x, ref y); // needs and updates x/y
// more local code...
等等。其中DoSomethingSpecific
是一个私有方法,只是为了让方法的职责可管理而移到外面。
任何时候,如果你想改变一个值类型的值——这在你想要高效地更新一对相关的值的情况下经常发生(即不返回包含两个int的结构体,而是传递(ref int x,ref int y))。
struct Foo
{
int i;
public void Test()
{
i++;
}
}
static void update(ref Foo foo)
{
foo.Test();
}
并且
Foo b = new Foo();
update(ref b);
在这里,您需要使用带有out
的两个参数,例如:
static void update(Foo foo, out Foo outFoo) //Yes I know you could return one foo instead of a out but look below
{
foo.Test();
outFoo = foo;
}
Foo
的方法,使用 out
和 ref
时会得到两倍的参数。另一种选择是返回N元组。我没有一个现实世界的例子来说明何时使用这些东西。 .TryParse
方法返回 Nullable< T >
,也可以避免使用 out
,这本质上是一个 boolean * T
的元组。??
这样用户才会知道。我用了扩展方法,就像这样:jf (Request["SomeHeader"].ToBoolean() == true)
,我觉得它很不错。它也可以在标题为空时工作,但可能不太清楚它确实这样做了。 - Lasse Espeholt当你需要在大数上进行高效的原地算法时,它非常有用。
除了swap<>之外,另一个有用的例子是:
Prompter.getString("Name ? ", ref firstName);
Prompter.getString("Lastname ? ", ref lastName);
Prompter.getString("Birthday ? ", ref firstName);
Prompter.getInt("Id ? ", ref id);
Prompter.getChar("Id type: <n = national id, p = passport, d = driver licence, m = medicare> \n? ", ref c);
public static class Prompter
{
public static void getKey(string msg, ref string key)
{
Console.Write(msg);
ConsoleKeyInfo cki = Console.ReadKey();
string k = cki.Key.ToString();
if (k.Length == 1)
key = k;
}
public static void getChar(string msg, ref char key)
{
Console.Write(msg);
key = Console.ReadKey().KeyChar;
Console.WriteLine();
}
public static void getString(string msg, ref string s)
{
Console.Write(msg);
string input = Console.ReadLine();
if (input.Length != 0)
s = input;
}
public static void getInt(string msg, ref int i)
{
int result;
string s;
Console.Write(msg);
s = Console.ReadLine();
int.TryParse(s, out result);
if (result != 0)
i = result;
}
// not implemented yet
public static string getDate(string msg)
{
// I should use DateTime.ParseExact(dateString, format, provider);
throw new NotImplementedException();
}
}
在这里使用out不是一个选项
getTreeNodeValues(ref selectedValue, ref selectedText);
编辑:
正如评论所说,最好在这里使用“out”。
getTreeNodeValues(out selectedValue, out selectedText);
我正在使用它来处理对象:
MyCar car = new MyCar { Name="TestCar"; Wieght=1000; }
UpdateWeight(ref car, 2000);
out
关键字。在这里使用ref
并不太合适。 - Jeff Mercadoout
。但你也应该考虑使用一个辅助对象的选项。如果有超过两个返回值,则使用辅助对象通常是更清晰的策略。 - Timwi