从数组中获取通用枚举器

Jay*_*Fix 83 c# arrays generics ienumerator

在C#中,如何从给定数组中获取通用枚举器?

在下面的代码中,MyArray是一个MyType对象数组.我想以MyIEnumerator所示的方式获得,但似乎我获得了一个空的调查员(尽管我已经证实了这一点MyArray.Length > 0).

MyType [ ]  MyArray  =  ... ;
IEnumerator<MyType>  MyIEnumerator
  =  ( MyArray.GetEnumerator() as IEnumerator<MyType> ) ;
Run Code Online (Sandbox Code Playgroud)

Meh*_*ari 90

适用于2.0+:

((IEnumerable<MyType>)myArray).GetEnumerator()
Run Code Online (Sandbox Code Playgroud)

适用于3.5+(花哨的LINQy,效率稍差):

myArray.Cast<MyType>().GetEnumerator()   // returns IEnumerator<MyType>
Run Code Online (Sandbox Code Playgroud)

  • 正如@Mehrdad所暗示的那样,使用`LINQ/Cast`具有完全不同的运行时行为,因为**数组**的每个元素都将通过一组额外的`MoveNext`和`Current`枚举器往返传递,并且如果阵列很大,这可能会影响性能.在任何情况下,通过首先获得适当的枚举器(即,使用我的答案中所示的两种方法之一),可以完全且容易地避免该问题. (7认同)
  • 首先是更好的选择!不要让任何人让自己欺骗"花哨的LINQy"版本,这完全是多余的. (6认同)
  • LINQy代码实际上返回Cast方法结果的枚举器,而不是数组的枚举器... (3认同)

Gle*_*den 48

你可以自己决定是否有足够的魔法来保证一个无关的库调用:

int[] arr;
IEnumerator<int> Get1()
{
    return ((IEnumerable<int>)arr).GetEnumerator();  // <-- 1 non-local call

    // ldarg.0 
    // ldfld int32[] foo::arr
    // castclass System.Collections.Generic.IEnumerable`1<int32>
    // callvirt instance class System.Collections.Generic.IEnumerator`1<!0> System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
}

IEnumerator<int> Get2()
{
    return arr.AsEnumerable().GetEnumerator();   // <-- 2 non-local calls

    // ldarg.0 
    // ldfld int32[] foo::arr
    // call class System.Collections.Generic.IEnumerable`1<!!0> System.Linq.Enumerable::AsEnumerable<int32>(class System.Collections.Generic.IEnumerable`1<!!0>)
    // callvirt instance class System.Collections.Generic.IEnumerator`1<!0> System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
}
Run Code Online (Sandbox Code Playgroud)

并且为了完整性,还应该注意以下内容不正确 - 并且将在运行时崩溃 - 因为T[]选择通用IEnumerable接口用于其默认(即非显式)实现GetEnumerator().

IEnumerator<int> NoGet()                    // error - do not use
{
    return (IEnumerator<int>)arr.GetEnumerator();

    // ldarg.0 
    // ldfld int32[] foo::arr
    // callvirt instance class System.Collections.IEnumerator System.Array::GetEnumerator()
    // castclass System.Collections.Generic.IEnumerator`1<int32>
}
Run Code Online (Sandbox Code Playgroud)

问题是,为什么不SZGenericArrayEnumerator<T>继承SZArrayEnumerator- 当前标记为"密封"的内部类 - 因为这将允许(协变)通用枚举器默认返回?


gre*_*man 23

由于我不喜欢施法,所以稍微更新一下:

your_array.AsEnumerable().GetEnumerator();
Run Code Online (Sandbox Code Playgroud)

  • 不同之处在于,它是在编译时完成的隐式转换,而不是在运行时完成的显式转换。因此,如果类型错误,您将遇到编译时错误,而不是运行时错误。 (3认同)

lea*_*eat 6

为了使它尽可能干净,我喜欢让编译器完成所有工作.没有演员表(所以它实际上是类型安全的).没有使用第三方库(System.Linq)(没有运行时开销).

    public static IEnumerable<T> GetEnumerable<T>(this T[] arr)
    {
        return arr;
    }
Run Code Online (Sandbox Code Playgroud)

//并使用代码:

    String[] arr = new String[0];
    arr.GetEnumerable().GetEnumerator()
Run Code Online (Sandbox Code Playgroud)

这利用了一些编译器魔法,可以保持一切清洁.

另一点需要注意的是,我的答案是进行编译时检查的唯一答案.

对于任何其他解决方案,如果"arr"的类型发生更改,则调用代码将进行编译,并在运行时失败,从而导致运行时错误.

我的答案将导致代码无法编译,因此我在代码中发送错误的机会较少,因为它会向我发出信号,表明我使用了错误的类型.

  • @t3chb0t 不,他们不是。运行时的工作**因为**我们知道 `Foo[]` 实现了 `IEnumerable&lt;Foo&gt;`,但是如果它发生变化,它在编译时不会被检测到。显式强制转换永远不是编译时证明。而是分配/返回数组作为 IEnumerable&lt;Foo&gt; 使用隐式转换,这是编译时证明。 (2认同)