gdo*_*ica 339 .net c# resharper performance
在我的代码中需要使用IEnumerable<>几次因此得到Resharper错误"可能的多个枚举IEnumerable".
示例代码:
public List<object> Foo(IEnumerable<object> objects)
{
if (objects == null || !objects.Any())
throw new ArgumentException();
var firstObject = objects.First();
var list = DoSomeThing(firstObject);
var secondList = DoSomeThingElse(objects);
list.AddRange(secondList);
return list;
}
Run Code Online (Sandbox Code Playgroud)
objects参数List,然后避免可能的多次枚举,但后来我没有得到我能处理的最高对象. IEnumerable到List在方法的开头: public List<object> Foo(IEnumerable<object> objects)
{
var objectList = objects.ToList();
// ...
}
Run Code Online (Sandbox Code Playgroud)
但这只是尴尬.
在这种情况下你会做什么?
Pau*_*ell 454
IEnumerable作为参数的问题在于它告诉呼叫者"我希望枚举这个".它没有告诉他们你想要枚举多少次.
我可以将objects参数更改为List,然后避免可能的多次枚举,但后来我没有得到我能处理的最高对象.
采取最高目标的目标是高尚的,但它为太多的假设留下了空间.你真的希望有人将LINQ to SQL查询传递给这个方法,只为你枚举它两次(每次得到可能不同的结果吗?)
这里缺少的语义是,调用者可能没有花时间阅读方法的细节,可能假设您只迭代一次 - 因此他们会传递给您一个昂贵的对象.您的方法签名不表示任何一种方式.
通过将方法签名更改为IList/ ICollection,您至少可以使调用者更清楚您的期望是什么,并且可以避免代价高昂的错误.
否则,大多数查看该方法的开发人员可能会假设您只迭代一次.如果采取一个IEnumerable非常重要的,你应该考虑.ToList()在方法的开头做.
遗憾的是.NET没有IEnumerable + Count + Indexer的接口,没有添加/删除等方法,这是我怀疑会解决这个问题的方法.
Mar*_*ell 30
如果您的数据总是可重复的,也许不用担心.但是,您也可以将其展开 - 如果传入的数据很大(例如,从磁盘/网络读取),这尤其有用:
if(objects == null) throw new ArgumentException();
using(var iter = objects.GetEnumerator()) {
if(!iter.MoveNext()) throw new ArgumentException();
var firstObject = iter.Current;
var list = DoSomeThing(firstObject);
while(iter.MoveNext()) {
list.Add(DoSomeThingElse(iter.Current));
}
return list;
}
Run Code Online (Sandbox Code Playgroud)
注意我稍微改变了DoSomethingElse的语义,但这主要是为了显示展开的用法.例如,您可以重新包装迭代器.你也可以把它变成一个迭代器块,这可能很好; 然后没有list- 你会yield return得到它们的项目,而不是添加到要返回的列表.
Nis*_*nga 11
..及之后,您可以尝试确定序列中的元素数量,而无需使用Enumerable.TryGetNonEnumeratedCount(IEnumerable, Int32)方法强制枚举。
如果无需枚举即可确定 的true计数,则此方法返回;source否则,false。因此您可以检查是否需要进一步实施。
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
IEnumerable<int> arrayOne = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var canGetCountDirectly = arrayOne.TryGetNonEnumeratedCount(out int theCount);
Console.WriteLine($"Count can be returned directly = {canGetCountDirectly}");
Console.WriteLine($"Count = {theCount}");
}
}
Run Code Online (Sandbox Code Playgroud)
在方法签名中使用IReadOnlyCollection<T>或IReadOnlyList<T>来代替IEnumerable<T>,其优点是可以明确表明您可能需要在进行迭代之前检查计数,或出于某些其他原因而进行多次迭代。
但是,它们有很大的缺点,如果您尝试将代码重构为使用接口,例如会使其对动态代理更具可测试性和友好性,则会导致问题。关键点是它IList<T>不继承自IReadOnlyList<T>,并且对于其他集合及其各自的只读接口也类似。(简而言之,这是因为.NET 4.5希望保持与早期版本的ABI兼容性。但是他们甚至没有抓住机会在.NET Core中进行更改。)
这意味着,如果您IList<T>从程序的某个部分得到一个并将其传递给期望一个的另一部分,IReadOnlyList<T>则不能!不过,您可以将传递IList<T>为IEnumerable<T>。
最后,IEnumerable<T>是所有.NET集合(包括所有集合接口)支持的唯一只读接口。当您意识到自己将自己锁定在某些架构选择之外时,任何其他选择都会再次吸引您。因此,我认为在函数签名中使用它来表达您只想要一个只读集合是正确的类型。
(请注意,IReadOnlyList<T> ToReadOnly<T>(this IList<T> list)如果基础类型同时支持两个接口,则始终可以编写一个简单的转换扩展方法,但是在重构时,IEnumerable<T>必须始终在任意位置手动添加它,因为它们始终兼容。)
与往常一样,这不是绝对的,如果您正在编写大量数据库的代码,而意外的多次枚举将成为灾难,那么您可能会希望选择其他折衷方案。
| 归档时间: |
|
| 查看次数: |
124066 次 |
| 最近记录: |