在C#中,你在哪里使用参数前面的“ref”?

关于“ref”和“out”参数的定义已经存在许多问题,但它们看起来像是糟糕的设计。 您是否认为ref是正确的解决方案?

看起来你总能做一些更干净的事情。 有人能给我一个例子,说明这将成为问题的“最佳”解决方案吗?

在我看来, ref很大程度上弥补了宣布新实用程序类型的难度以及对现有信息“处理信息”的困难,这是C#自从其通过LINQ,generics和匿名类型创建以来已经迈出的巨大步骤。 。

所以不,我认为不再有很多明确的用例了。 我认为这很大程度上是语言最初设计的遗留物。

我确实认为它仍然有意义(如上所述)你需要从函数返回某种错误代码以及返回值,但没有别的(所以更大的类型不是真正合理的。如果我在项目中的所有地方都这样做,我可能会为thing-plus-error-code定义一些通用的包装器类型,但在任​​何给定的实例中, refout都可以。

好吧, ref通常用于特殊情况,但我不会称之为冗余或C#的遗留function。 你会看到它(和out )在XNA中使用了很多。 在XNA中, Matrix是一个struct而且是一个相当庞大的struct (我相信64个字节),如果你将它传递给函数使用ref来避免复制64个字节,但只有4个或8个,这通常是最好的。专家C#function? 当然。 不再使用或表示糟糕的设计? 我不同意。

一个领域是使用小实用function,例如:

 void Swap(ref T a, ref T b) { T tmp = a; a = b; b = tmp; } 

我在这里看不到任何“更清洁”的选择。 当然,这不完全是架构级别。

P / Invoke是我唯一能想到你必须使用ref或out的地方的地方。 在其他情况下,它们可以很方便,但就像你说的那样,通常还有另一种更清洁的方式。

如果您想要返回多个对象 ,那么由于某些未知原因不会将它们绑定到一个对象中 ,该怎么办?

 void GetXYZ( ref object x, ref object y, ref object z); 

编辑: divo建议使用OUT参数更适合这个。 我不得不承认,他有一点意见。 我将把这个答案留在这里作为记录,这是一个不合适的解决方案。 在这种情况下,OUT胜过REF。

我认为最好的用途是那些你经常看到的用途; 你需要同时拥有一个值和一个“成功指标”,它不是一个函数的例外。

ref有用的一种设计模式是双向访问者。

假设您有一个可用于加载或保存各种基本类型值的Storage类。 它处于Load模式或Save模式。 它有一组称为Transfer的重载方法,这是一个处理int值的例子。

 public void Transfer(ref int value) { if (Loading) value = ReadInt(); else WriteInt(value); } 

对于其他原始类型,会有类似的方法 – boolstring等。

然后在一个需要“转移”的类上,你会写一个这样的方法:

 public void TransferViaStorage(Storage s) { s.Transfer(ref _firstName); s.Transfer(ref _lastName); s.Transfer(ref _salary); } 

这种相同的方法既可以从Storage加载字段,也可以将字段保存到Storage ,具体取决于Storage对象所处的模式。

实际上,您只是列出了需要转移的所有字段,因此它非常接近声明式编程而非强制性。 这意味着您不需要编写两个函数(一个用于读取,一个用于写入),并且假设我在这里使用的设计是依赖于顺序的,那么确定字段将始终被读取非常方便/以相同的顺序书写。

一般的观点是,当参数被标记为ref ,您不知道该方法是读取它还是写入它,这允许您设计在两个方向之一工作的访问者类,以对称的方式调用(即访问方法不需要知道访问者类在哪个方向模式下操作)。

比较:属性+reflection

为什么这样做而不是归因于字段并使用reflection来自动实现TransferViaStorage的等价物? 因为有时候reflection很慢就会成为一个瓶颈(但总是要确定这一点 – 它几乎不是真的,属性更接近于声明性编程的理想)。

真正的用途是在创建结构时。 C#中的结构是值类型,因此在按值传递时始终完全复制。 如果需要通过引用传递它,例如出于性能原因或者因为函数需要对变量进行更改,则可以使用ref关键字。

我可以看到某人是否有一个包含100个值的结构(显然已经存在问题),您可能希望通过引用传递它以防止100个值复制。 然后返回那个大型结构并写入旧值可能会出现性能问题。

使用“ref”关键字的明显原因是您希望通过引用传递变量。 例如,将System.Int32之类的值类型传递给方法并更改其实际值。 更具体的用途可能是您想要交换两个变量。

 public void Swap(ref int a, ref int b) { ... } 

使用“out”关键字的主要原因是从方法返回多个值。 我个人更喜欢将值包装在专门的结构或类中,因为使用out参数会产生相当丑陋的代码。 用“out”传递的参数 – 就像“ref” – 通过引用传递。

 public void DoMagic(out int a, out int b, out int c, out int d) { ... } 

必须使用’ref’关键字时,有一个明显的例子。 如果对象已定义但未在您打算调用的方法范围之外创建,并且您要调用的方法应该使用'new'来创建它,则必须使用'ref' 。 例如{object a; Funct(a);} {Funct(object o) {o = new object; o.name = "dummy";} {object a; Funct(a);} {Funct(object o) {o = new object; o.name = "dummy";} {object a; Funct(a);} {Funct(object o) {o = new object; o.name = "dummy";}不会对object 'a'做任何事情,也不会在编译或运行时抱怨它。 它不会做任何事情。 {object a; Funct(ref a);} {Funct(object ref o) {o = new object(); o.name = "dummy";} {object a; Funct(ref a);} {Funct(object ref o) {o = new object(); o.name = "dummy";}将导致'a'成为名为"dummy ”的新对象。 但是如果'new'已经完成,那么ref不需要(但如果提供则有效)。 {object a = new object(); Funct(a);} {Funct(object o) {o.name = "dummy";}