C# 为什么列表和IEnumerable有两种完全不同的反向版本?
对于C# 为什么列表和IEnumerable有两种完全不同的反向版本?,c#,linq,list,ienumerable,reverse,C#,Linq,List,Ienumerable,Reverse,对于列表对象,我们有一个名为的方法。 它颠倒了列表“就地”的顺序,不返回任何内容 对于IEnumerable对象,我们有一个名为的扩展方法。 它返回另一个IEnumerable 我需要以相反的顺序遍历一个列表,所以我不能直接使用第二种方法,因为我得到一个列表,我不想反转它,只是向后迭代 所以我可以这样做: for(int i = list.Count - 1; i >=0; i--) 或 我发现它的可读性不如我有一个IEnumerable,就这么做吧 foreach(var item i
列表
对象,我们有一个名为的方法。它颠倒了列表“就地”的顺序,不返回任何内容 对于
IEnumerable
对象,我们有一个名为的扩展方法。它返回另一个IEnumerable 我需要以相反的顺序遍历一个列表,所以我不能直接使用第二种方法,因为我得到一个列表,我不想反转它,只是向后迭代 所以我可以这样做:
for(int i = list.Count - 1; i >=0; i--)
或
我发现它的可读性不如我有一个IEnumerable,就这么做吧
foreach(var item in list.Reverse())
我不明白为什么这两个方法以相同的名称以这种方式实现。这是相当烦人和混乱的
为什么没有一个名为BackwardsIterator()的扩展来代替Reverse()来处理所有IEnumerable
我对这个选择的历史原因非常感兴趣,不仅仅是“如何做”之类的东西 值得注意的是,list方法比extension方法旧得多。命名可能与
Reverse
相同,似乎比BackwardsIterator
更简洁
如果要绕过列表版本并转到扩展方法,则需要将列表视为IEnumerable
:
请注意,
Enumerable
版本需要完全迭代底层的可枚举项,以便开始反向迭代。如果您计划在相同枚举的情况下多次执行此操作,请考虑永久反转该命令并正常迭代。 然后编写自己的后台站点。p>
public static IEnumerable BackwardsIterator(this List lst)
{
for(int i = lst.Count - 1; i >=0; i--)
{
yield return lst[i];
}
}
List.Reverse
的存在早于IEnumerable.Reverse
的存在。它们被命名为相同的原因是。。。无能这是一个可怕的错误;显然,LinqIEnumerable
函数应该有一个不同的名称。。。e、 例如,向后
。。。因为它们有完全不同的语义。实际上,它为程序员设置了一个可怕的陷阱——有人可能会将list
的类型从list
更改为Collection
,然后突然list.Reverse()
,而不是就地反转列表
,只返回一个被丢弃的IEnumerable
。MS给这些方法起相同的名字是多么无能,怎么强调都不为过
为了避免这个问题,您可以定义自己的扩展方法
public static IEnumerable<T> Backwards<T>(this IEnumerable<T> source) => source.Reverse();
公共静态IEnumerable backward(此IEnumerable源)=>source.Reverse();
您甚至可以添加一个特例来高效处理可索引列表:
public static IEnumerable<T> Backwards<T>(this IEnumerable<T> source) =>
source is IList<T> list ? Backwards<T>(list) : source.Reverse();
public static IEnumerable<T> Backwards<T>(this IList<T> list)
{
for (int x = list.Count; --x >= 0;)
yield return list[x];
}
向后公开静态IEnumerable(此IEnumerable源)=>
来源是IList列表?向后(列表):source.Reverse();
公共静态IEnumerable向后(此IList列表)
{
对于(int x=list.Count;--x>=0;)
收益回报表[x];
}
你可以使用Enumerable。Reverse(list)你为什么不把你的列表键入你喜欢的类型,然后它就会使用你喜欢的方法版本呢?^大概他“喜欢”它是一个列表,因为它是一个列表
——但是,正如他清楚地说的,他不想使用列表。Reverse
。“键入到您喜欢的类型”意味着强制转换它,这并不比使用AsEnumerable()
更漂亮。如果您要从中生成实用方法,您应该使用更有效的方法(即反向for循环)<代码>反向
作为IEnumerable,效率相当低,因为它需要迭代可枚举项,将其存储在缓冲区中,然后反向迭代缓冲区。@Servy你是对的。。特别是当列表很大时。!。。thx指出事实上,即使一个人只想枚举一次,并且想保持原始列表不受干扰,我希望调用ToArray()
然后在结果数组上调用Reverse()
比使用Enumerable.Reverse()
更快。List.ToArray()
方法可以将数据作为块存储在目标数组中,而不必处理单个项,而Enumerable.Reverse()方法
将--除非它对源是列表的情况有特殊处理
--必须在反转项目之前将项目一次复制到数组中。@Supercat很可能它有特殊的大小写。我在过去使用过ILSpy的一些其他方法,它们在IList
上很多时候都是特例。确实有很多特例,但也有一些恼人的皱纹。例如,尽管可以将列表
传递给需要IEnumerable
的代码,但此类代码将无法利用任何特殊的“列表特性”。我的“几乎可以肯定”语言可能有点强,但在枚举之前转换为数组的一个优点是,如果我回忆正确,C#在使用foreach
循环处理它知道是数组的东西时会生成特殊代码。即使Enumerable.Reverse
返回临时数组的枚举数,编译器也不会知道这一点。@supercat这绝对是一个好消息,另外,您还可以从不依赖当前linq实现的内部工作中获得任何性能提升的好处。还有numbers.Reverse()
,哪一个扩展的重载。
public static IEnumerable BackwardsIterator(this List lst)
{
for(int i = lst.Count - 1; i >=0; i--)
{
yield return lst[i];
}
}
public static IEnumerable<T> Backwards<T>(this IEnumerable<T> source) => source.Reverse();
public static IEnumerable<T> Backwards<T>(this IEnumerable<T> source) =>
source is IList<T> list ? Backwards<T>(list) : source.Reverse();
public static IEnumerable<T> Backwards<T>(this IList<T> list)
{
for (int x = list.Count; --x >= 0;)
yield return list[x];
}