5

我需要一个已经设置了几个属性值的小部件。我需要更改小部件的名称。我被选项 3 所吸引,但我很难说出原因。

public void Do(Widget widget) { // 1
    widget.Name = "new name";
}

public void Do(ref Widget widget) { // 2
    widget.Name = "new name";
}

public Widget Do(Widget widget) { // 3
    widget.Name = "new name";
    return widget;
}

我想用几个问题来玩《恶魔代言人》并收集答案,以帮助我解释为什么我会被选项 3 所吸引。

选项 1:为什么直接修改传入的 Widget?您只是“返回”一个对象。为什么不直接使用传入的对象?

选项 2:为什么返回 void?为什么不直接在签名中传达您将使用指向参数对象本身的实际内存指针?

选项 3:您返回的对象与传入的对象相同,这对您来说不是很奇怪吗?

4

4 回答 4

12

选项 1:这是最常见的方法 -如果您不想修改引用本身,则不需要 ref。确保适当地命名您的方法,以便期望传递的对象确实被修改。

选项 2:这仅在您想更改传递的引用本身时有用,即创建一个新Widget实例或将引用设置为指向现有小部件(如果您想保持实例的总数较低,这可能很有用,如果它们全部具有相同的属性,请参阅Flyweight 模式,通常返回的小部件在这种情况下应该是不可变的,但您可以使用工厂代替)。在您的情况下,这似乎不合适。

选项 3:这允许流畅的“构建器”方法 - 也有它的好处,即链接到 Widget 的更改,有些人认为这些更改更具表现力和自我记录。另请参阅Fluent 界面

于 2011-09-22T15:52:12.500 回答
6

选项1:

美好的。正是我会使用的。您可以改变小部件的内容,但不能改变小部件本身。

选项 2:

不。不。不。你不修改引用本身,所以你不需要使用ref. 如果您修改了引用本身(例如,widget = new Widget()那么out/ref是正确的选择,但根据我的经验,这很少需要。

选项 3:
与选项 1 类似。但可以链接到流式 API。我个人不喜欢这个。如果我返回一个副本并且保持原始对象不变,我只会使用该签名。


但这里最重要的是如何命名该方法。该名称需要清楚地暗示原始对象已发生变异。

在许多情况下,我会选择选项 4:使类型不可变并返回一个副本。但是对于显然是实体而不是值的小部件,这是没有意义的。

于 2011-09-22T15:53:01.913 回答
4

这三个选项之间实际上没有任何功能差异。(存在差异,只是没有与您的问题相关。)保持简单 - 使用选项 1。

于 2011-09-22T15:53:00.003 回答
1

我认为选项 1 和选项 3 都是可行的选择。选项 3 具有自记录的好处,因为它意味着您正在修改方法中的 Widget。我认为最糟糕的选择是选项 2。ref对我来说,关键字意味着您正在修改对象的引用,而您肯定不会这样做。

于 2011-09-22T15:56:29.893 回答