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
的存在。它们被命名为相同的原因是。。。无能这是一个可怕的错误;显然,Linq
IEnumerable
函数应该有一个不同的名称。。。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];
}