1

我见过这样的方法:

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;
}

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

4

5 回答 5

7

在您给出的示例中,第一个对我来说似乎比第二个好得多。如果我看到一个接受列表并返回列表的方法,我的第一个假设是它正在返回一个新列表并且没有触及给定的列表。因此,第二种方法具有意想不到的副作用。

只要您的方法命名得当,修改参数就没有什么危险。考虑一下:

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

使用像“Fill”这样的名称,您可以非常确定该方法将修改列表。

于 2009-03-07T06:20:23.403 回答
1

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

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

我对 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可以修改 s 的情况下,我认为使用第一种方法没有问题。

于 2009-03-07T06:25:17.183 回答
0

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

在我看来,这真的取决于你在做什么。只需确保您的文档清楚地说明正在发生的事情。如果您喜欢这种事情,请编写前置条件和后置条件。

于 2009-03-07T06:20:51.780 回答
0

扩展方法的出现使得处理引入副作用的方法变得更加容易。例如,在您的示例中,说起来变得更加直观

public static class Extensions
{
  public static void AddBar(this List<string> list)
  {
     list.Add("Bar");
  }
}

并调用它

mylist.AddBar();

这更清楚地表明列表正在发生某些事情。

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

于 2009-03-07T07:16:27.060 回答
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参数。

于 2009-03-07T08:57:02.357 回答