Zaf*_*iro 47 generics .net-3.5
我一直听说.net 3.5你应该在List上使用IEnumerable,但我找不到任何参考资料或文章来解释为什么它更加精通.有谁知道解释这个的任何内容?
提出这个问题的目的是为了更好地理解IEnumerable在幕后做的事情.如果您能为我提供任何链接,我将进行研究并发布答案.
Gre*_*g D 70
IEnumerable<T>是一个由.实现的接口List<T>.我怀疑你听到IEnumerable<T>应该使用的原因是因为它是一个不那么紧缩的接口要求.
例如,请考虑以下方法签名:
void Output(List<Foo> foos)
{
foreach(var foo in foos) { /* do something */ }
}
Run Code Online (Sandbox Code Playgroud)
此方法要求传递List的具体实现.但它只是按顺序做某事.它实际上并不需要随机访问或任何其他List<T>甚至是IList<T>给它的东西.相反,该方法应该接受IEnumerable<T>:
void Output(IEnumerable<Foo> foos)
{
foreach(var foo in foos) { /* do something */ }
}
Run Code Online (Sandbox Code Playgroud)
现在我们使用支持我们所需操作的最通用(最不具体)接口.这是OO设计的一个基本方面.我们通过只需要我们需要的东西来减少耦合,而不仅仅需要其他东西.我们还创建了一个更灵活的方法,因为foos参数可能是a Queue<T>,a List<T>,实现的任何东西IEnumerable<T>.我们不是强迫调用者不必要地将他们的数据结构转换为List.
因此,它IEnumerable<T>不比"性能"或"运行时"方面的列表更有效.这IEnumerable<T>是一种更有效的设计结构,因为它更能说明您的设计需求.(虽然这可以在特定情况下导致运行时增益.)
Joe*_*orn 42
Enumerables有几个非常好的属性,在将它们转换为列表时会丢失.即他们:
首先,我将介绍延迟执行.弹出测验:以下代码将多少次迭代输入文件中的行?
IEnumerable<string> ReadLines(string fileName)
{
using (var rdr = new StreamReader(fileName) )
{
string line;
while ( (line = rdr.ReadLine()) != null) yield return line;
}
}
var SearchIDs = new int[] {1234,4321, 9802};
var lines = ReadLines("SomeFile.txt")
.Where(l => l.Length > 10 && l.StartsWith("ID: "));
.Select(l => int.Parse(l.Substring(4).Trim()));
.Intersect(SearchIDs);
Run Code Online (Sandbox Code Playgroud)
答案恰好是一个零.在迭代结果之前,它实际上不会做任何工作.您需要在打开文件之前添加此代码:
foreach (string line in lines) Console.WriteLine(line);
Run Code Online (Sandbox Code Playgroud)
即使在代码运行之后,它仍然只能循环一次.将其与您需要迭代此代码中的行的次数进行比较:
var SearchIDs = new int[] {1234,4321, 9802};
var lines = File.ReadAllLines("SomeFile.txt"); //creates a list
lines = lines.Where(l => l.Length > 10 && l.StartsWith("ID: ")).ToList();
var ids = lines.Select(l => int.Parse(l.Substring(4).Trim())).ToList();
ids = ids.Intersect(SearchIDs).ToList();
foreach (string line in lines) Console.WriteLine(line);
Run Code Online (Sandbox Code Playgroud)
即使您忽略File.ReadAllLines()调用并使用第一个样本中的相同迭代器块,第一个样本仍然会更快.当然,您可以将其编写为使用列表一样快,但要做到这一点,需要将读取文件的代码绑定到代码中解析它的代码.所以你失去了另一个重要的特征:可组合性.
为了展示可组合性,我将添加一个最终功能 - 无界系列.考虑以下情况:
IEnumerable<int> Fibonacci()
{
int n1 = 1, n2 = 0, n;
yield return 1;
while (true)
{
n = n1 + n2;
yield return n;
n2 = n1;
n1 = n;
}
}
Run Code Online (Sandbox Code Playgroud)
这看起来会永远存在,但是您可以使用IEnumerable 的可组合性属性来构建安全地给出前50个值或者小于给定数字的每个值的东西:
foreach (int f in Fibonacci().Take(50)) { /* ... */ }
foreach (int f in Fibonacci().TakeWhile(i => i < 1000000) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)
最后,IEnumerable更灵活.除非你绝对需要能够附加到列表或通过索引访问项目,否则你几乎总是更好地编写函数来接受IEnumerables作为参数而不是列表.为什么?因为如果需要,您仍然可以将列表传递给函数 - 列表是 IEnumerable.就此而言,数组也是如此,许多其他集合类型都很好.因此,通过在这里使用IEnumerable,您可以使用完全相同的函数并使其更强大,因为它可以处理更多不同类型的数据.
IEnumerable<T>是不效率比一List<T>作为List<T> 是一个IEnumerable<T>.
该IEnumerable<T>接口是简单地使用.NET程序的方式迭代器模式,仅此而已.
此接口可以在许多类型(List<T>包括)上实现,以允许这些类型返回迭代器(即实例IEnumerator<T>),以便调用者可以迭代一系列项.