为什么我会使用Enumerable.ElementAt()与运算符?
这似乎是一个愚蠢的问题,但我还没有find答案,所以在这里。 🙂
在这两种情况下,如果您未能检查收集范围,将会收到“超出范围”的例外情况。 这只是编码风格偏好?
如果有人需要一个例子:
List<byte> myList = new List<byte>(){0x01, 0x02, 0x03}; byte testByte = myList.ElementAt(2);
与
byte testByte = myList[2];
因为Enumerable
更通用,并且由enumerable表示的集合可能没有索引器。
但是,如果这样做 – 不要使用ElementAt()
它可能不会有效。
ElementAt()
为C#中的所有枚举提供统一的接口。 我倾向于经常使用它,因为我喜欢通用的API。
如果基础types支持随机访问(即它支持[]
运算符),那么ElementAt将使用它。 所以唯一的开销是一个额外的方法调用(这几乎是不相关的)。
如果底层types不支持随机访问,则ElementAt()
通过迭代枚举来模拟它,直到它到达您关心的元素。 这可能是非常昂贵的,甚至有副作用。
还有ElementAtOrDefault()
,通常非常方便。
更新 :我觉得这是一个有趣的话题,并决定写博客 。
基本上,我的观点是,在不需要IList<T>
情况下利用抽象来进行随机访问是非常有意义的。 如果你正在编写代码,想要通过索引来随机访问集合的元素,那么只需要一个代码即可。 如果你需要一个IList<T>
开始,我真的看不到使用ElementAt
而不是索引器。
但是,这只是我的看法,当然。
不要将它用于像T[]
或List<T>
这样的IList<T>
实现。 如果您需要为不提供随机访问的集合types(例如Queue<T>
或Stack<T>
,则仅使用它。
var q = new Queue<int>(); var s = new Stack<int>(); for (int i = 0; i < 10; ++i) { q.Enqueue(i); s.Push(i); } // q[1] would not compile. int fromQueue = q.ElementAt(1); int fromStack = s.ElementAt(1);
避免在某些情况下使用ElementAt()!
如果您知道要查找每个元素,并且您已经(或者可能有)超过500个,那么只需调用ToArray(),将其存储在可重用的数组variables中,然后将其索引。
例如; 我的代码是从Excel文件中读取数据。
我使用ElementAt()来查找我的单元格引用的SharedStringItem。
用500或更less的线,你可能不会注意到差异。
有了16K线,这需要100秒。
更糟糕的是(每行读取的)索引越大,每次迭代索引就越多,所以花费的时间比应该多。
前1000行花了2.5秒,而最后的1000行花了10.7秒。
通过这一行代码循环:
SharedStringItem ssi = sst.Elements<SharedStringItem>().ElementAt(ssi_index);
导致此logging输出:
...Using ElementAt(): RowIndex: 1000 Read: 1,000 Seconds: 2.4627589 RowIndex: 2000 Read: 1,000 Seconds: 2.9460492 RowIndex: 3000 Read: 1,000 Seconds: 3.1014865 RowIndex: 4000 Read: 1,000 Seconds: 3.76619 RowIndex: 5000 Read: 1,000 Seconds: 4.2489844 RowIndex: 6000 Read: 1,000 Seconds: 4.7678506 RowIndex: 7000 Read: 1,000 Seconds: 5.3871863 RowIndex: 8000 Read: 1,000 Seconds: 5.7997721 RowIndex: 9000 Read: 1,000 Seconds: 6.4447562 RowIndex: 10000 Read: 1,000 Seconds: 6.8978011 RowIndex: 11000 Read: 1,000 Seconds: 7.4564455 RowIndex: 12000 Read: 1,000 Seconds: 8.2510054 RowIndex: 13000 Read: 1,000 Seconds: 8.5758217 RowIndex: 14000 Read: 1,000 Seconds: 9.2953823 RowIndex: 15000 Read: 1,000 Seconds: 10.0159931 RowIndex: 16000 Read: 1,000 Seconds: 10.6884988 Total Seconds: 100.6736451
一旦我创build了一个中间数组来存储SharedStringItem进行引用,我的时间就是100秒到10秒,现在在相同的时间内处理每一行。
这行代码:
SharedStringItem[] ssia = sst == null ? null : sst.Elements<SharedStringItem>().ToArray(); Console.WriteLine("ToArray():" + watch.Elapsed.TotalSeconds + " Len:" + ssia.LongCount());
并通过这一行代码循环:
SharedStringItem ssi = ssia[ssi_index];
导致此logging输出:
...Using Array[]: ToArray(): 0.0840583 Len: 33560 RowIndex: 1000 Read: 1,000 Seconds: 0.8057094 RowIndex: 2000 Read: 1,000 Seconds: 0.8183683 RowIndex: 3000 Read: 1,000 Seconds: 0.6809131 RowIndex: 4000 Read: 1,000 Seconds: 0.6530671 RowIndex: 5000 Read: 1,000 Seconds: 0.6086124 RowIndex: 6000 Read: 1,000 Seconds: 0.6232579 RowIndex: 7000 Read: 1,000 Seconds: 0.6369397 RowIndex: 8000 Read: 1,000 Seconds: 0.629919 RowIndex: 9000 Read: 1,000 Seconds: 0.633328 RowIndex: 10000 Read: 1,000 Seconds: 0.6356769 RowIndex: 11000 Read: 1,000 Seconds: 0.663076 RowIndex: 12000 Read: 1,000 Seconds: 0.6633178 RowIndex: 13000 Read: 1,000 Seconds: 0.6580743 RowIndex: 14000 Read: 1,000 Seconds: 0.6518182 RowIndex: 15000 Read: 1,000 Seconds: 0.6662199 RowIndex: 16000 Read: 1,000 Seconds: 0.6360254 Total Seconds: 10.7586264
正如你所看到的,转换成一个arrays需要33,560个物品的一小部分时间,是非常值得的,以加快我的导入过程。
ElementAt()
确实试图像IList<T>
使用索引器,所以使用ElementAt()
性能可能与直接使用索引器的性能差别不大。 所以即使你有一个IList<T>
,你也可能要考虑ElementAt()
,如果将来有可能改变你声明的types。
通过[]
使用ElementAt()
的唯一原因是,如果您有或需要IEnumerable
而不是IList
。
IEnumerable
不支持直接索引访问,如果你知道你有一个列表,继续使用第二种forms,因为它更容易阅读(可以说)。
所有枚举都支持ElementAt()
,而不仅仅是列表 – 性能明智的是它们在IList
types(即generics列表)上使用时相同,但是如果使用索引访问的列表更具performance力。 如果使用reflection器查看ElementAt()
的源代码,您将看到如果IEnumerable
是IList
types的,它将在内部使用索引访问:
.. IList<TSource> list = source as IList<TSource>; if (list != null) { return list[index]; } ..
从语义的angular度来看,它们都完全一样。 无论你使用它不会改变程序的意义。
你用[]
:
-
如果枚举被声明为
IList<T>
,因为:一个。 它更具可读性,惯用性和广泛的理解。 我使用它的第一个理由。
湾 你想优化每一寸。 因为它是一个直接查找并避免了
ElementAt
( 注意,如果IEnumerable<T>
是一个IList<T>
,ElementAt
使用索引器,因此它不是一个很大的收益 )。C。 你仍然在.NET 3.5时代。
你使用ElementAt
:
-
如果声明的types只是一个
IEnumerable<T>
。 -
如果
ElementAt
使其更具可读性。 例如,在stream利的风格调用中:var x = GetThatList().ElementAt(1).Wash().Spin().Rinse().Dry(); //is more readable than var x = GetThatList()[1].Wash().Spin().Rinse().Dry();
-
如果你真的坚持使用
IList<T>
和IEnumerable<T>
的一致风格。
简单地说,如果你有variables声明为IList<T>
使用[]
,否则使用ElementAt
( 我强调点“声明”,因为即使你的IEnumerable<T>
是一个IList<T>
底下,你只能使用ElementAt
。投射到IList<T>
使用索引器是一个否定的(它发生在ElementAt
里面) )。 可读性胜利。 如果你有这个简单的select。 索引器的风格是我必须在绝大多数情况下使用。 我不得不使用ElementAt
很less。