修改引用类型参数的方法是不是很糟糕?

时间:2009-03-07 06:15:58

标签: c# side-effects

我见过这样的方法:

public void Foo(List<string> list)
{
    list.Add("Bar");
}

这是修改方法中参数的好习惯吗?

这不会更好吗?

public List<string> Foo(List<string> list)
{
    // Edit
    List<string> newlist = new List<string>(list);
    newlist.Add("Bar");
    return newlist;
}

感觉就像第一个例子有意想不到的副作用。

5 个答案:

答案 0 :(得分:7)

在你给出的例子中,第一个似乎比第二个更好。如果我看到一个方法接受了一个列表并且还返回了一个列表,那么我的第一个假设就是它返回一个新列表并且没有触及它所给出的列表。因此,第二种方法是具有意想不到的副作用的方法。

只要您的方法被恰当地命名,修改参数几乎没有危险。考虑一下:

public void Fill<T>(IList<T> list)
{
    // add a bunch of items to list
}

使用“Fill”之类的名称,您可以非常肯定该方法将修改列表。

答案 1 :(得分:1)

坦率地说,在这种情况下,两种方法或多或少都做同样的事情。两者都将修改传入的List

如果目标是通过这样的方法使列表不可变,则第二个示例应该复制发送的List,然后对新的Add操作执行List操作1}}然后返回。

我不熟悉C#或.NET,所以我的猜测是:

public List<string> Foo(List<string> list)
{
    List<string> newList = (List<string>)list.Clone();
    newList.Add("Bar");
    return newList;
}

这样,调用Foo方法的方法将返回新创建的List,并且不会触及传入的原始List

这实际上取决于您的规范或API的“合同”,因此在可以修改List的情况下,我认为第一种方法没有问题。

答案 2 :(得分:0)

你在两种方法中都做了完全相同的事情,只有其中一种方法返回相同的列表。

在我看来,这取决于你在做什么。只需确保您的文档清楚了解正在发生的事情。如果您涉及这类事情,请写出前置条件和后置条件。

答案 3 :(得分:0)

扩展方法的出现使得处理引入副作用的方法变得容易一些。例如,在您的示例中,说

会变得更直观
public static class Extensions
{
  public static void AddBar(this List<string> list)
  {
     list.Add("Bar");
  }
}

并用

调用它
mylist.AddBar();

更清楚地表明列表中发生了某些事情。

正如评论中所提到的,这对列表最有用,因为对列表的修改可能会更加混乱。在一个简单的对象上,我倾向于只修改对象。

答案 4 :(得分:0)

将列表作为参数的方法修改列表实际上并不意外。如果您想要一个只从列表中读取的方法,您将使用仅允许读取的接口:

public int GetLongest(IEnumerable<string> list) {
    int len = 0;
    foreach (string s in list) {
        len = Math.Max(len, s.Length);
    }
    return len;
}

通过使用这样的接口,您不仅禁止该方法更改列表,而且还可以更灵活,因为它可以使用任何实现接口的集合,例如字符串数组。

其他一些语言有一个const关键字,可以应用于参数以禁止方法更改它们。由于.NET具有可用于此的接口和不可变的字符串,因此实际上不需要const个参数。