为什么在 System.ServiceModel.Channels.Message 中要使用 "ref" 关键字?

3

我现在正在查看一些C#代码,我想确认一下我对它应该如何工作的理解是否正确。

这与传递System.ServiceModel.Channels.Message有关。每个接受Message对象的方法都有类似于以下的方法签名:

void SomeMethod(ref Message message) { ... }

我不理解的是为什么要加入 "ref" 关键字。据我了解,如果该方法不会完全替换对象,则不需要使用它。

void SomeMethod(ref Message message)
{
    message = new Message();
}

但是,如果消息仅向标头添加其他内容或仅从对象中读取值,则不需要使用 "ref" 关键字,因为消息是引用类型。

void SomeMethod(Message message)
{
    message.Headers.Add("Some Data");  // This should be fine?
}

干杯


同意代码为什么需要一个 ref 参数并不明显。也许一些额外的上下文会有所帮助。 - Jon
恐怕这就是我所拥有的全部了,我已经查看了MS文档,似乎每个接受此类型对象的方法都需要使用"ref"关键字? - daz-fuller
1个回答

8
首先,你可能是正确的;很有可能编写代码的人错误地添加了“ref”。
但我想利用这个机会确保你清楚地理解“ref”的含义。它意味着“将此变量设置为别名”。也就是说,当你这样说时:
M(ref string x) { x = null; }
N(string x) { x = null; }
...
string y = "abc";
N(y);
M(ref y);

对于N(y)的调用意味着复制y中的引用并将该引用放入x中。而对于M(ref y)的调用意味着x和y现在都是同一个变量的名称。也就是说,x成为y的别名

非常不幸的是,我们选择了“ref”作为关键字,因为它立即使人们对引用类型和值类型感到困惑。 它与引用类型和值类型完全无关。“ref”的意思是“我正在引用另一个变量”。

refout之间的区别仅仅是ref要求在调用之前初始化别名变量,但out则不需要。 (两者都保证在调用正常完成后变量将被初始化


1
我实际上倾向于同意选择“ref”,因为当与引用类型一起使用时,它表示双重间接(“引用到引用”)。相反,如果关键字是“alias”、“var”或“byname”等,则语义上的相似性可能成为绊脚石... - Alan
2
@Alan:我理解你的观点。但是,“ref”确实表达了一种实现细节。这个事实,即这是对包含存储位置引用的存储位置的引用,从编译器开发人员的角度来看很有趣,但对用户来说并没有太多意义,毕竟用户没有其他方法利用这个实现细节。 - Eric Lippert
1
我看到你的了。事实上,经过一段时间后,人们往往会混淆真正的语义(比如“引用类型”)和特定的实现(比如“引用存储位置”)...但是,使用“别名”可能会导致更少的误解,对于“普通”的程序员而言,而编译器编写者已经知道细节了。 - Alan
所以为了确保我正确地理解你们两个(顺便感谢两位的答案),使用ref更类似于在C/C++中获取指针,因此在这种情况下,SomeMethod方法看起来更像是“SomeMethod (Message ** message)”。我想,对于这个问题的困惑更多地是因为MSDN文档中到处都分散着“ref”关键字,唯一可能的原因是因为Message类实现了IDisposable,并且它可能正在尝试防止对象提前被处理? - daz-fuller
1
@daz-fuller:实际上,在System.ServiceModel.Channels中,我并没有看到太多'ref Message'参数的使用。对于在方法内创建消息并返回布尔值的情况,有几种具有'out Message'属性的方法,这是多个返回值的经典案例;而当确实存在'ref Message'时,它被用于在方法内有条件地替换消息为另一个后代消息,这与处理显然没有关系。 - Alan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接