默认使用ref关键字传递引用类型

4
我与一位开发人员合作,他默认使用ref关键字传递引用类型,例如StringBuilder、string和MemoryStream。无论是否需要实际更改引用本身,他都会这样做。
public void ExampleMethod(ref MemoryStream ms)
{
    byte b=ms.ReadByte();
    ...
    // No changing of actual ms reference such as: ms=new MemoryStream();
}  

几乎总是,这些方法只是使用对象并在任何方式下都不改变引用就返回。对于不可变类型,比如字符串,这有时是必要的,但为什么对于可变类型也是如此呢?
对我来说,这有点像“代码异味”,因为它可能导致代码不易维护,因为它比实际需要更加宽容。
然而,这是否严重到我需要与开发人员提出?我的初步感觉是需要,但也许这太过苛求了?

2
@Ash - 建议开发人员阅读http://www.yoda.arachsys.com/csharp/parameters.html - Russ Cam
5个回答

18

也许你可以问那位开发者为什么这样做...也许他(错误地)认为这样更高效?也许那位开发者是一位C++开发者,认为使用ref和在C++中使用指针类似?

如果他来自C++背景,我会问他为什么这样做,并向他解释这完全没有必要。它不会提高性能,而且给该方法带来了不同的语义,因为它允许更改引用,只有在确实需要该方法更改引用时才应使用ref关键字。

我认为这并不过分。即使是经验丰富的开发者也不知道他正在使用的新语言的所有细节。当你向他解释时,他将学到新的东西,可能会很感激。 :)


@Frederik,是的,他们来自C++背景。我认为你对性能的看法可能是正确的。 - Ash

6
它绝对值得提出来。我已经多次见过这种情况,通常是由于开发人员不理解类型系统而导致的 - 这是编写正确和可维护代码的关键部分。
我会找到一个明显不必要但他们想要改变对象本身内容的例子 - 一些附加到 StringBuilder 的东西将是理想的。然后,礼貌地询问为什么他们选择使用 ref 修饰符。指出它没有使用也能很好地工作。
随时可以参考我的参数文章和我的引用/值类型文章

谢谢Jon,你的文章应该对开发者很有帮助,因为他们听说过“Jon Skeet”和StackOverflow。 - Ash

4
我认为这并不过于追求完美,因为开发人员可能误解了“ref”关键字的作用。它应该在绝对必要的情况下使用,表示该方法将保证或最有可能交换您的引用与其他内容 - 而不是“仅仅为了好玩”。 更新
至于如何进行沟通...嗯,这取决于他将ref放在所有事物上的动机。如果他认为这会提高性能,则可以使用StopWatch.StartNew()编写一个小程序,并将内容传递到带有和不带有ref的方法中。我没有尝试过,但认为性能差异可能在于创建新引用,而那应该非常小。
除此之外,应该按照它们的意图和表达意图来使用功能。当我看到带有refout参数的方法时,我期望它们要么完全更改传入的实例,要么该方法初始化传入的对象。不为这些目的使用这些关键字只是一种混淆其他程序员的方式,通过在方法调用中添加不必要的关键字并可能设置有关所使用的API的错误期望。

@Frank,现在我遇到了一个程序员通常会遇到的难题,就是如何向他清晰地传达这个问题,而不陷入辩论。我想我需要一些事实和证据。 - Ash

3
看起来是一个误解,即需要使用ref关键字来传递引用到方法。你应该询问开发者是否是这种情况。
当不需要使用ref关键字时,唯一好的理由是允许稍后更改方法以替换对象。当然,这只应在您真正预期需要它的情况下使用。通常您应该只编写当前要求的代码,而不是您可以想象的每种可能的要求。
如果您发现没有任何原因需要使用ref,则应将其从代码中删除。遵循封装原则,方法不应比所需的参数值具有更多的控制权。
通常情况下,ref关键字很少被使用。在大多数情况下,可以使用更面向对象的方法代替。它有时用于值类型的性能原因,例如在Double.TryParse方法中,但实际上从不与引用类型一起使用。

-1

ref 关键字仅适用于修改值类型(任何从 ValueType 派生的类型)。 值类型是诸如 intbytecharstructfloat 等的东西。 对于对象引用或类(例如 Stream 后代或 StringBuilder),使用 ref 没有意义,而且绝对是代码异味。

我唯一看到需要将 StringBuilder 引用作为 ref 传递的情况是在将字符串值与 Win32 进行交换时。


我认为楼主已经知道了。:) 他的问题有点不同。 :) - Frederik Gheysels
1
@Mike,有一些明显合理的情况(我同意并不常见),在引用类型上使用ref是完全合理的。经典的例子是一个交换函数,用于交换实际的引用。 - Ash
在某些情况下,对于引用类型使用 ref/out 是有意义的。一个例子是 Dictionary.TryGetValue(key, out value) - Jon Skeet
如果我将变量A的引用传递到函数中,我传递的是一个可以被更新为指向其他对象的内存“位置”。我的想法是,实质上我不是在改变变量A,而是在改变它所指向的东西。我必须同意Ash和Jon的观点,我的错! - Mike J

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