如果未更改对集合的引用,是否应该返回集合?

时间:2014-04-02 13:40:16

标签: c# java c++ oop

我有一个接受如下收集的方法

 public IList<CountryDto> ApplyDefaults(IList<CountryDto> dtos)
        {
            //Iterates the collection
                //Validates the items in collection
                //If items are invalid
                //Removes items e.g dtos.Remove(currentCountryDto)

            return dtos;//Do I need to do this?
        }

我的问题是,因为我不应该更改对集合的引用,我应该从方法中再次返回集合吗?

  1. For:通过返回集合,我在签名中明确表示,并且用户知道集合中的项目可能与原始源不同。排序可以避免歧义。
  2. 反对:由于验证不会更改集合的引用,因此在技术上返回它是没有意义的。
  3. 在这种情况下,最好的方法是什么?
    注意:我不确定这个问题是否基于意见。我想我可能在设计方面遗漏了一些东西。

7 个答案:

答案 0 :(得分:2)

在每种编程语言中,您自己的代码/库与核心库的方法的一致性具有很高的价值。因此,检查如何定义Collections.sort()或Collection.swap()和Collections.shuffle(),如果您打算修改输入参数,我建议不要返回输入参数。此外,您的方法应该以这样的方式命名,很明显输入参数被修改。否则,您的方法将被视为有副作用。

返回一个值通常表明它是一个反映工作的新实例,由方法执行或在builders的情况下用于方法链。

答案 1 :(得分:1)

我宁愿在详细案例中回复boolean(或enum:收藏保存完整已更改无法验证等。)

// true if the collection is changed, false otherwise
public Boolean ApplyDefaults(IList<CountryDto> dtos) {
  Boolean result = false;
  //Iterates the collection
  //Validates the items in collection
  //If items are invalid:
  //  Removes items e.g dtos.Remove(currentCountryDto)
  //  result = true;
  ...
  return result; 
}

...

if (ApplyDefaults(myData)) {
  // Collection is changed, do some extra stuff
}

答案 2 :(得分:1)

鉴于您的意见/要求:

  1. 如果应用默认值,则无需报告。
  2. ApplyDefaults很复杂并且调用其他服务而不是用于生成流畅的API
  3. ApplyDefaults是一个“黑匣子”;注入验证逻辑,因此调用代码不知道/不关心验证
  4. 我认为基于这些,这个方法肯定应该返回对传入列表的引用,即使没有应用验证。首先,除非API明确地围绕方法链接(您表示您不想要),否则返回List<T>类型通常表示正在创建 new 列表。其次,如果新列表创建,用户可能会发现自己以他们不期望的方式修改列表。

    考虑:

    IList<CountryDto> originalCountries = Service.GetCountries();
    IList<CountryDto> validatedCountries = ApplyDefaults(originalCountries);
    validatedCountries.Add(mySpecialCountry);
    
    OutputOriginalCountries(originalCountries);
    OutputValidatedCountries(validatedCountries);
    

    这段代码不是很特别,而且是一种相当常见的模式。如果ApplyDefaults返回对同一originalCountries集合的引用,则mySpecialCountry也会添加到originalCountries。这将违反最小惊讶原则。

    如果此行为根据项目是否经过验证/过滤而发生变化,则会加剧这种情况。由于验证逻辑是调用者不知道或不关心的黑盒行为,因此API使用者可能依赖于它是否返回相同的引用。他们要么必须进行自己的参考检查(例如if (myValidatedCountries == myInputCountries)),要么每次都复制一次。无论如何,这成为程序员在使用API​​时必须兼顾的另一种奇怪的行为。

    我认为该方法应该:

    A)始终返回包含过滤掉的项目(public IList<CountryDto> ApplyDefaults(IEnumerable<CountryDto> dtos)

    的复制列表

    B)就地修改传入列表(public void ApplyDefaults(IList<CountryDto> dtos)

    对于选项A,根据列表的大小,这会导致每次创建复制列表时可能不必要的工作,即使没有执行过滤也是如此。但是,验证/过滤逻辑可能更简单。您可以使用LINQ查询来很好地应用过滤。此外,从列表中删除项通常很昂贵,因为它必须重建内部数组。因此,构建新列表实际上可能更快。您甚至可以将此处的签名简化为IEnumerable<CountryDto>;这允许更广泛的使用,并且非常显然你正在创建一个新的集合。

    对于选项B,如果不需要验证,则不进行任何工作,并且该方法基本上是“免费的”(没有数组重建,没有复制,没有引用更改)。但是,如果有重要的验证,删除方面可能是昂贵的。由于您不是方法链接,因此 应该具有void返回类型,因为对于开发人员而言,这是在原地修改列表更为明显。这遵循其他常见的方法,如List<T>.Sort。此外,如果用户希望拥有单独的originalCountriesvalidatedCountries,他们可以随时制作副本:

    var validatedCountries = originalCountries.ToList();
    ApplyDefaults(validatedCountries);
    

    最终,您选择哪一个可能取决于性能。如果验证/删除很便宜且很少,那么就地修改列表可能是最好的。如果您期望对列表进行大量更改,则每次生成新副本可能会更快。

    无论如何,我建议你更清楚地说明方法的名称。例如:

    public IList<CountryDto> GetValidCountries(IEnumerable<CountryDto> dtos)
    
    public void RemoveInvalidCountries(IList<CountryDto> dtos)
    

    当然,根据您的实际代码上下文,命名可能会有所不同(我怀疑ApplyDefaults是一个公共/继承的方法名称,并不是特定于CountryDto)< / p>

答案 3 :(得分:0)

首先:你不能改变你通过参数发送的集合的引用,因为默认情况下你会获得它的副本。您需要使用ref关键字才能更改它。

其次:如果你的方法有一个返回类型,那么它必须返回一个对象。您的方法不是GetNewCollectionWithAppliedDefaults,而是ApplyDefaults,这意味着将修改集合。您应该返回boolean true / false以通知用户更改已完成,或者始终返回参数&#39; s collecion(以允许嵌套方法调用)。

另外,为什么你认为退回收藏品没有意义?我说没有反对它的论据。转过来问题:&#34;为什么我不会退回收藏品并且可能会损害我的代码&#34;?

答案 4 :(得分:0)

从技术上讲,我会说两者之间差别不大。

然而,正如您所指出的,常用的约定是函数应该只返回它创建的对象。基本上,这意味着返回一个对象的函数正在生成一个函数而一个不返回任何内容的函数正在修改作为参数传递的对象。

同样,这只是一个约定,并没有在C#社区中广泛使用,但在python社区中,例如,它是。

有些人会返回一个布尔值(或错误代码)作为错误的指示符(就像旧的dos命令行一样)。我不喜欢这种方法,并且宁愿提出我以后可以处理的例外情况。

最后,我认为最好的方法是返回一个值,该值指示函数是否进行了更改,并最终返回一个值,指示已完成了多少更改。它可以是布尔值,也可以是插入/删除元素的数量......

在任何情况下,尽量不要在所有代码中,至少在单个项目中,尝试与您选择的方法保持一致。有时,除了遵守队友使用的惯例外,别无选择。

答案 5 :(得分:0)

(我的回答是基于Java观点; C ++和C#程序员可能有不同的看法。)我认为最好回归该集合。您返回的集合与给定的集合相同的事实只是一个实现细节,在未来的代码版本中,您可能希望更改它。返回的集合的文档可能与传入的集合不同。

另一方面,如果您想锁定此方法修改集合的设计,请以这种方式记录并且不要返回集合。我不喜欢这样做,但我可以在某些情况下看到优势。

答案 6 :(得分:0)

在你的情况下,我会留空,因为ApplyDefaults清楚地说明它在做什么。 此外,在集合本身中应用ApplyDefaults可能是个好主意。子类IList或List或其他什么,然后你这样调用:

myCollection.ApplyDefaults();

这很明显。