这两种方法有区别吗?
public class A
{
public int Count { get; set; }
}
public A Increment(A instance)
{
instance.Count++;
return instance;
}
public void Increment(A instance)
{
instance.Count++;
}
我的意思是,除了一个方法返回相同的引用而另一个方法没有返回任何内容之外,它们都完成了同样的事情,增加了作为参数传递的引用的Count
属性。
使用一个对另一个有优势吗?我通常倾向于使用前者,因为方法链接,但是有性能权衡吗?
例如,后一种方法的一个优点是无法创建新的引用:
public void Increment(A instance)
{
instance.Count++;
instance = new A(); //This new object has local scope, the original reference is not modified
}
这可以被视为针对接口的新实现的防御性方法。
我不希望这是基于意见的,所以我明确地寻找从文档或语言规范中取出的具体优势(或缺点)。
答案 0 :(得分:2)
例如,后一种方法的一个优点是无法创建新的引用。
您可以考虑其中一个缺点。考虑:
public A Increment(A instance)
{
return new A { Count = instance.Count +1 };
}
或者
public A Increment()
{
return new A { Count = this.Count +1 };
}
始终如一地应用此功能,您可以使A
类具有不可变性,并带来所有优势。
它还允许返回实现相同接口的不同类型。这就是Linq的工作原理:
Enumerable.Range(0, 1) // RangeIterator
.Where(i => i % 2 == 0) // WhereEnumerableIterator<int>
.Select(i => i.ToString()) // WhereSelectEnumerableIterator<int, string>
.Where(i => i.Length != 1) // WhereEnumerableIterator<string>
.ToList(); // List<string>
虽然每个操作都对IEnumerable<int>
类型起作用,但每个结果都是由不同的类型实现的。
如你所说,变异流畅的方法在C#中非常罕见。它们在没有C#支持的属性的语言中更常见,因为它很方便:
someObject.setHeight(23).setWidth(143).setDepth(10);
但是在C#中,这种setXXX
方法很少见,属性设置器更常见,而且它们不能流畅。
主要的例外是StringBuilder
,因为它的本质意味着在不同的值上反复调用Append()
和/或Insert()
非常常见,而且流畅的风格很适合这一点。
否则,变异流畅方法并不常见这一事实意味着你提供的所有东西都是返回该领域的额外成本。这是微不足道的,但是当它与更加惯用的C#风格一起使用时,它将无法获得任何东西。
要有一个外部方法,既可以变异也可以返回变异对象,这可能会导致某人认为你没有变异对象,因为你要返回结果。
见到:
public static IList<T> SortedList(IList<T> list);
使用代码的人可能会认为在调用list
之后,而不是排序到位,并且两者会不同并且可以单独变异。
仅仅因为这个原因,要么返回一个新对象,要么返回void
以使变异性更明显。
我们可以在返回新对象时使用快捷方式:
public static T[] SortedArray<T>(T[] array)
{
if (array.Length == 0) return array;
T[] newArray = new T[array.Length];
Array.Copy(array, newArray, array.Length);
Array.Sort(newArray);
return newArray;
}
这里我们利用了这样一个事实,即由于空数组基本上是不可变的(它们没有要变异的元素,并且它们不能添加到它们),对于大多数用途来说,返回相同的数组与返回一个新数组相同阵列。 (通过返回string
,与ICloneable.Clone()
实现this
的方式进行比较。除了减少完成的工作量外,我们还减少了分配数量,从而减少了GC压力。即使在这里,我们也需要小心(有人在对象标识的集合上会因此受到阻碍),但在许多情况下它会很有用。
答案 1 :(得分:1)
简短回答 - 这取决于。
长答案 - 如果您使用构建器模式或需要链接方法,我会考虑返回对象的实例。
大多数其他情况看起来像代码味道:如果您控制API并且发现很多地方没有使用您返回的对象,那么为什么还需要额外的努力呢?可能你会创造微妙的错误。