【问题标题】:Are methods that modify reference type parameters bad?修改引用类型参数的方法不好吗?
【发布时间】:2010-10-11 21:46:59
【问题描述】:

我见过这样的方法:

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

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

【问题讨论】:

  • 我想你的意思是 List newlist = new List(list);
  • 谢谢。我已经更新了。

标签: c# side-effects


【解决方案1】:

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

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

并调用它

mylist.AddBar();

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

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

【讨论】:

  • 这似乎回答了实际问题,即第一个样式有问题,因为它并没有让人一眼看出Foo方法改变了传入的参数。但是,写的想法一个类,对于我可能想在列表中添加/更改的每个值都有一个方法是疯狂的。
  • 我考虑的是列表,而不是一般访问。我发现我的初级开发人员比普通对象更频繁地从列表中获得副作用。话虽如此,使用只读属性和设置方法是有思路的。
【解决方案2】:

实际上,以列表为参数的方法会修改列表,这并不出人意料。如果你想要一个只从列表中读取的方法,你会使用一个只允许读取的接口:

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

【讨论】:

    【解决方案3】:

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

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

    我不熟悉 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 的“合同”,因此在可以修改 Lists 的情况下,我认为使用第一种方法没有问题。

    【讨论】:

      【解决方案4】:

      您在这两种方法中执行完全相同的操作,只是其中一种返回相同的列表。

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

      【讨论】:

        【解决方案5】:

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

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

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

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

        【讨论】:

        • 我的问题是我已经养成了期望方法永远不会修改参数的习惯。不知道我是怎么养成这种习惯的。
        • 你应该研究一下 F# - 听起来函数式编程很适合你。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-09-08
        • 2017-05-06
        • 2011-08-06
        • 2022-06-15
        • 2018-12-12
        • 2023-03-17
        • 1970-01-01
        相关资源
        最近更新 更多