【问题标题】:Using IEnumerable<T> type in parameter of method在方法的参数中使用 IEnumerable<T> 类型
【发布时间】:2012-03-31 15:13:31
【问题描述】:

以前我使用IEnumerable&lt;T&gt; 类型,如果我将集合作为方法的参数传递。

但最近,我遇到了以类似方式创建的IEnumerable&lt;T&gt; 类型集合的问题:

var peoples = names.Select(name => new People(name));

在这种情况下,总是,如果我使用 peoples 集合(例如,foreach),它会创建 People 类的新实例,并且很容易导致错误。

所以我想问一下使用IEnumerable &lt;T&gt;类型参数是否正确。我认为它可能会导致问题(参见上面的示例),不应使用这种类型。您推荐哪些替代方案(ICollection&lt;T&gt;IList&lt;T&gt; 等)以及何时使用哪种替代方案?

或者你认为这是一个愚蠢的问题,因为在Select方法中创建对象只使用了一个傻瓜?

当然,我知道我可以使用ToArray()ToList() 从而解决问题。但是其他人使用这种方法,就无法知道了。我想知道如何通过选择正确的类型参数来防止这种情况。当我只想“枚举”对象时,列表或数组对我来说太具体了。

【问题讨论】:

  • 使用new People(name)会出现什么样的错误?
  • Wiktor Zychla:问题:当我使用 peoples 时,它总是会创建 People 类的新实例。

标签: c# collections parameters ienumerable


【解决方案1】:

IEnumerable 不是一个集合。它只是你可以“枚举”的东西。问题不在于将IEnumerable 传递给您的方法,问题在于如果您使用LINQ(Select 方法),每次读取可枚举时它都会再次执行代码。如果只想让它执行一次,可以使用ToArray()ToList() 方法:

var peoples = names.Select(name => new People(name)).ToList();

像这样你仍然可以将它传递给任何接受IEnumerable(或List)的方法,它只会为每个人创建一个实例。

编辑: 你的方法不应该担心这些问题。是调用者的问题。使用可枚举而不是列表来调用您的方法可能有充分的理由。调用者应该知道,如果将枚举传递给不同的方法会给出不同的结果,因此您不必担心。

唯一的例外是如果您在方法本身中多次枚举参数。在这种情况下,您应该将参数缓存在方法内的列表中,然后根据需要多次枚举该列表。

【讨论】:

  • 这并不完全准确,LINQ 确实使用延迟执行,因此在您尝试从可枚举中提取某些内容之前它不会真正执行,这与您的答案不谋而合。但它不会在您每次访问可枚举时重新运行查询。因此,您可以将 LINQ 语句放在 foreach 的循环表达式中,它只会运行一次查询。微软竭尽全力优化这一点。
  • 是的,但是如果您使用 2 个 foreach 语句访问可枚举的 if 将重新评估查询两次。
  • 我想我错过了这个问题涉及两个 foreach 语句的部分。抱歉,伙计,我不是刻薄,只是想确保我们给出准确的答案。
  • 我编辑了问题:当然,我知道我可以使用 ToArray() 或 ToList() 从而解决问题。但是其他人使用这种方法,就无法知道了。我想知道如何通过选择正确的类型参数来防止这种情况。当我只想“枚举”对象时,列表或数组对我来说太具体了。
  • @MV1893 这是您的呼叫者问题,而不是您的方法的问题。您的方法应该像以前一样枚举参数,而不用担心它是如何获得可枚举的。例外情况是您的方法多次枚举参数。在这种情况下,您应该将枚举缓存在方法内的列表中。
【解决方案2】:

ToArrayToList 的建议比人们最初想象的要多。我们很想将此建议视为简单地说,在呼叫站点或方法中的第一件事上调用 ToList/ToArray 可以解决问题,但您的问题是 IEnumerable&lt;T&gt; 是否合适 - 您可以从IEnumerable&lt;T&gt; 的参数类型更改为其他类型(如ICollection&lt;T&gt;),这使调用者有责任转换为实现此接口的类型(注意T[]List&lt;T&gt;IList&lt;T&gt; 和@ 987654331@ 都可以)。这种方法的部分问题是这些接口表示可变集合,而IEnumerable&lt;T&gt; 宣传该方法枚举项目——这只是我不喜欢这种方法的原因之一。

如果潜在的错误根本不是真正的错误怎么办?也许调用者打算将这些作为防御性副本或哑数据对象 - 在后一种情况下,从某种角度来看,它可能效率低下,但要求他们制作副本也是如此 - 但在这个提议的用途中,它绝对不是错误。同样,一刀切的建议也不适合,因为 IEnumerable&lt;T&gt; 对象不必永远终止 - 但需要传入数组类型将意味着无限(即计算)或仅仅是大 IEnumerable&lt;T&gt;对象是不可能的。

无论如何,我认为您提出有关防御性编程的问题是正确的。但是,在这种情况下,我认为最好的解决方案是坚持使用IEnumerable&lt;T&gt;,并根据他们可能会在某些有限情况下引入错误的推测来教育而不是限制您的调用者。

转述一句话:

为了防止白痴会犯的问题而进行设计的问题在于白痴非常聪明。

希望这会有所帮助。干杯!

【讨论】:

  • 这不是一个错误,但期望的行为非常有趣的想法,我从来没有想过。谢谢,在这些情况下我会继续使用 IEnumerable 。你的报价是完美的。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多