[英]Ref Abuse: Worth Cleaning Up?
我继承了一些广泛而不必要地使用ref关键字的代码。 如果不使用ref ,原始开发人员显然担心对象会被克隆为原始类型,并且在编写50k +代码行之前没有费心去研究这个问题。
这与其他不良编码实践相结合,创造了一些表面上非常危险的情况。 例如:
Customer person = NextInLine();
//person is Alice
person.DataBackend.ChangeAddress(ref person, newAddress);
//person could now be Bob, Eve, or null
你能想象走进商店改变你的地址,走出一个完全不同的人吗?
可怕,但在实践中,在这个应用程序中使用ref似乎是无害的多余。 我无法证明清理它需要花费大量时间。 为了帮助推销这个想法,我提出了以下问题:
我特别关心维护。 具有实例的合理答案是优选的。
我们也欢迎你争辩说没有必要进行清理。
我想说最大的危险是如果参数在函数内部由于某种原因设置为null
:
public void MakeNull(ref Customer person)
{
// random code
person = null;
return;
}
现在,你不仅仅是一个与众不同的人,你已经完全被淘汰了!
只要开发此应用程序的任何人都理解:
默认情况下,对象引用按值传递。
和:
使用
ref
关键字,对象引用通过引用传递。
如果代码现在按预期工作,并且您的开发人员了解其中的差异,则可能不值得将其全部删除。
我只是添加了我见过的最关键的ref关键字,方法看起来像这样:
public bool DoAction(ref Exception exception) {...}
是的,你必须声明并传递一个Exception引用才能调用该方法,然后检查方法的返回值,看看是否已经通过ref异常捕获并返回了异常。
你能弄清楚为什么代码的发起者认为他们需要将参数作为参考吗? 是因为他们确实更新了它然后删除了功能,还是因为他们当时不理解c#?
如果你认为清理是值得的,那么继续吧 - 特别是如果你现在有时间的话。 当真正的问题出现时,您可能无法正确地修复它,因为它很可能是一个紧急的错误修复,您将没有时间做正确的工作。
在C#中修改方法中参数的值是很常见的,因为它们通常是按值而不是参考。 这适用于参考和价值类型; 例如,将引用设置为null将更改原始引用。 当其他开发人员像往常一样工作时,这可能会导致非常奇怪和痛苦的错误。 使用ref参数创建递归方法是不行的。
除此之外,你对refs传递的内容有限制。 例如,您不能传递常量值,只读字段,属性等,因此在使用ref参数调用方法时需要许多辅助变量。
最后但并非最不重要的是性能,如果可能不太好,因为它需要更多的间接(ref只是一个需要在每次访问时解决的参考)并且还可以使对象保持更长时间,因为引用不会消失范围很快。
对我来说,闻起来就像一个C ++开发人员做出了无根据的假设。
我会谨慎地改变那些有效的东西。 (我假设它有效,因为你没有评论它被破坏,只是它很危险)。
你要做的最后一件事是打破一些微妙的东西,并且必须花一周的时间来追踪问题。
我建议你随时清理 - 一次一种方法。
虽然您遇到的具体问题可能比大多数情况更严重,但您的情况非常普遍 - 拥有大量代码库,不符合我们目前对“ 正确方法 ”的理解。
批发“升级”经常遇到困难。 随你进行重构 - 在处理它们时提高规格 - 更加安全。
这是建筑行业的先例。 例如,除非出现问题,否则不需要触摸较旧(例如19世纪)建筑物中的电线。 当有一个问题,虽然,新的工作已完成,以现代化的标准。
如何不必要地使用ref是破坏性的?
其他答案涉及语义问题,这绝对是最重要的事情。 好的代码是自我记录的,当我给出ref参数时,我认为它会改变。 如果没有,则API无法自我记录。
但为了好玩,我们如何看待另一个方面 - 性能?
void ChangeAddress(ref Customer person, Address address)
{
person.Address = address;
}
这里person
是对引用的引用,因此每当您访问它时都会引入一些间接。 让我们看看这可能转化为的一些组件:
mov eax, [person] ; load the reference to person.
mov [eax+Address], address ; assign address to person.Address.
现在非ref版本:
void ChangeAddress(Customer person, Address address)
{
person.Address = address;
}
这里没有间接,所以我们可以摆脱一个阅读:
mov [person+Address], address ; assign address to person.Address.
在实践中,人们希望.NET在ref
版本中缓存[person]
,在多次访问中分摊间接成本。 实际上,除了像这里的琐碎方法之外,指令数量实际上不会下降50%。
我会尝试解决它。 只需用正则表达式执行解决方案宽字符串替换,然后检查单元测试。 我知道这可能会破坏代码。 但是你多久使用一次参考? 几乎没有,对吗? 鉴于开发人员不知道它是如何工作的,我认为它在某个地方(它应该的方式)使用的可能性更小。 如果代码中断 - 好吧,回滚......
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.