【问题标题】:Using ref to indicate parameter will be modified使用 ref 表示参数将被修改
【发布时间】:2015-06-14 13:13:54
【问题描述】:

我理解 ref 意味着当方法返回时,提交的引用可能指向一个完全不同的对象。

然而,我喜欢 ref 修饰符的一点是,开发人员会立即知道,当方法返回时,他输入的内容可能会有所不同,因为调用方也需要 ref 修饰符。

从一个假设的 ORM 中采用一个简单的方法:

public Boolean AddItem(Entity someEntity)
{
  try
  {
    // Add item to database
    // Get Id of entity back from database
    someEntity.Id = *returnedId*;
    return true;
  }
  catch (DBException ex)
  {
    return false;
  }
}

该方法的任何调用者可能不知道他们的实体已通过调用该方法进行了更新。但是,将 someEntity 设置为 ref 参数,它向开发人员表明他们提交的参数会有所不同,然后他们知道要深入研究文档/代码以找出它是如何更改的,而没有他们可能从未想过这样做的修饰符。

我知道这有点滥用 ref 修饰符,因为在上面的示例中实际上并不需要它,但是这样使用它实际上会给我带来什么问题吗?

【问题讨论】:

  • 通过将成员声明为 public,您决定它们可以被任何引用您的对象的外部对象访问和/或修改。如果它不应该被修改,它应该是私有的
  • 我相信如果AddItem作者想告诉调用者Hey, I won't be modiyfing the paremeter,他会使用一个结构作为参数。在 C++ 中,const 修饰符确保参数不会被修改。为什么它在 c# 中不可用?这是更好的解释:stackoverflow.com/questions/3263001/…
  • @Tobsey:“应该可以修改”和“应该在传递给它的方法内修改”之间存在差异。 OP 正试图以某种方式复制类似于 C++ 的 const 参数的东西。
  • @ArtyomNeustroev 这里没有要查看的代码。 OP正在询问一个概念。此外,我们不会审查假设的代码。不要通过去。不要收取 200 美元。
  • 我认为这是一个糟糕的主意。现在调用这些方法后,我需要检查我的非空引用是否突然变为空。解决这个问题的正确方法是使用不可变类型和/或正确的方法文档,IMO。

标签: c# theory language-design


【解决方案1】:

我认为这是滥用。假设Entity是引用类型,参数声明

bool AddItem(ref Entity someEntity)

表示该方法可能会将引用 someEntity 移动到“指向”一个完全不同的对象实例。

如果您所做的只是改变现有实例someEntity,请不要写ref。相反,使用名称(方法名称和参数名称)和文档来明确您将“改变”(更改)对象。 示例名称(您可以选择更好的名称,因为您知道实际代码):AddItemAndUpdateEntityID

使用ref的后果:

  • 调用者必须使用一个变量。他不能使用属性、方法调用或表达式求值的返回值
  • 调用者必须使用确切的类型,他不能传递SpecificEntity,比如说,SpecificEntity 派生自Entity
  • 您的方法的逻辑必须准备好其他线程(或您自己调用的其他方法)可能会更改ref 参数的标识。例如,如果您在方法顶部检查 someEntity == null 是否在您的方法的稍后位置,因为其他人可能已将引用移至其他位置,因此该方法可能已更改。

【讨论】:

  • 我没有意识到这些后果,太棒了,这正是我正在寻找的答案。命名/cmets 它是!
【解决方案2】:

然而,我喜欢 ref 修饰符的一点是,开发人员立即知道他输入的内容可能在方法返回时有所不同

不,他们没有。

他们对ref 修饰符的了解是,当方法返回时,参数实际上可能引用了else

将接受引用类型的方法更改为仅使用 ref 以便您给人一种错误的印象在任何方面都没有用。

当然也有另一面;除了滥用ref 来表明它不意味着什么,你已经失去了ref 来表明它确实 含义的能力;必须检查代码以查看该方法是否实际使用 ref,否则不会知道从一个方法调用到另一个方法您是否仍在处理同一个对象。

【讨论】:

  • 我想这可能会让不熟悉非常规编码风格的人感到困惑,感谢您的回答。
  • 这会让熟悉它的人感到困惑;他们仍然永远不会知道一个方法是否真的在使用ref
  • 同意,我很少(如果有的话?)找到理由将 ref 用于它实际提供的功能,这似乎是浪费!即使每个人都熟悉了,并被告知要在 cmets 中查看 ref 的正确用法(我知道这很疯狂),但出于 Jepp 解释的原因,这不是一个可行的选择,并且会坚持使用命名/cmets。
  • 我希望你很少使用终结器,或显式垃圾收集,或stackalloc 或许多其他在需要时非常有用但很少需要的东西。
猜你喜欢
  • 2020-12-02
  • 1970-01-01
  • 2019-12-12
  • 1970-01-01
  • 2016-04-05
  • 2011-12-15
  • 2014-11-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多