我需要一个已经设置了几个属性值的Widget。我需要更改小部件的名称。我倾向于第三种选择,但我很难说清楚原因。
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:你返回的是你传入的同一个对象,这对你来说是不是很奇怪?
选项1:这是最常见的方法-如果您不想修改引用本身,则不需要 ref。确保适当地命名方法,以便期望传递的对象确实被修改。
选项2:这只有在你想改变传递的引用本身时才有用,即创建一个新的Widget
实例或将引用设置为指向一个现有的小部件(如果你想保持实例的总数较低,如果它们都具有相同的属性,这可能是有用的,参见Flyweight模式,通常在这种情况下返回的小部件应该是不可变的,尽管你会使用工厂)。对你来说,这似乎不合适。
选项3:这允许一个流畅的"构建器"方法——也有它的好处,即链接到Widget的更改,有些人认为这更具表现力和自文档性。另请参见Fluent界面
选项1:
好。我也会这么用。您可以更改小部件的内容,但不能更改小部件本身。
选项2:
。不。不。您不需要修改引用本身,因此不需要使用ref
。如果您修改了引用本身(例如widget = new Widget()
),那么out
/ref
是正确的选择,但根据我的经验,这很少是必要的。
选项3:
类似于选项1。但是可以用流畅风格的API链接起来。我个人不喜欢这样。只有当我返回一个副本而不触及原始对象时,我才使用该签名。
但是这里最重要的是如何命名方法。该名称需要清楚地暗示原始对象发生了变化。
在许多情况下,我会选择选项4:使类型不可变并返回副本。但是对于那些明显是实体而不是值的小部件来说,这是没有意义的。这三个选项实际上没有任何功能上的区别。(有差异,只是没有一个与你的问题相关。)保持简单-使用选项1
我认为选项1和选项3都是可行的选择。选项3具有自文档化的优点,因为它意味着您正在修改方法中的Widget。我认为最糟糕的选择是选项2。ref
关键字对我来说意味着你正在修改对象的引用,而你肯定不会这样做。