我见过这样的方法:

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

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

有帮助吗?

解决方案

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

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

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

使用“Fill”等名称你可以非常肯定该方法会修改列表。

其他提示

坦率地说,在这种情况下,两种方法或多或少都做同样的事情。两者都将修改传入的 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 ,我认为第一种方法没有问题。

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

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

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

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 参数。

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

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

并用

调用它
mylist.AddBar();

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top