C# 从数组中获取泛型枚举数

C# 从数组中获取泛型枚举数,c#,arrays,generics,ienumerator,C#,Arrays,Generics,Ienumerator,在C#中,如何从给定数组中获取泛型枚举数 在下面的代码中,MyArray是MyType对象的数组。我想以所示方式获得MyIEnumerator, 但我似乎获得了一个空枚举数(尽管我已经确认MyArray.Length>0) MyType[]MyArray=; IEnumerator MyIEnumerator=MyArray.GetEnumerator()作为IEnumerator; 适用于2.0+: ((IEnumerable<MyType>)myArray).GetEnumera

在C#中,如何从给定数组中获取泛型枚举数

在下面的代码中,
MyArray
MyType
对象的数组。我想以所示方式获得MyIEnumerator
,
但我似乎获得了一个空枚举数(尽管我已经确认
MyArray.Length>0

MyType[]MyArray=;
IEnumerator MyIEnumerator=MyArray.GetEnumerator()作为IEnumerator;
适用于2.0+:

((IEnumerable<MyType>)myArray).GetEnumerator()
((IEnumerable)myArray).GetEnumerator()
适用于3.5+(花式LINQy,效率稍低):

myArray.Cast().GetEnumerator()//返回IEnumerator

由于我不喜欢铸造,有一点更新:

your_array.AsEnumerable().GetEnumerator();

你可以自己决定铸造是否丑陋到足以保证一个无关的库调用:

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()
}
神秘的是,为什么
szGenericarArrayEnumerator
不继承
SZArrayEnumerator
——一个当前标记为“密封”的内部类——因为这将允许默认返回(协变)泛型枚举数?


可能会执行得更好一些,因为它只需要检查类型,而不需要强制转换。

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

这利用了一些编译器的魔力,使一切保持干净

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

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

我的回答将导致代码无法编译,因此我在代码中发送错误的可能性较小,因为它会向我发出使用错误类型的信号。

MyType[]arr={new MyType(),new MyType(),new MyType();
    MyType[] arr = { new MyType(), new MyType(), new MyType() };

    IEnumerable<MyType> enumerable = arr;

    IEnumerator<MyType> en = enumerable.GetEnumerator();

    foreach (MyType item in enumerable)
    {

    }
IEnumerable enumerable=arr; IEnumerator en=enumerable.GetEnumerator(); foreach(可枚举中的MyType项) { }
当然,您所能做的就是为数组实现自己的通用枚举器

using System.Collections;
using System.Collections.Generic;

namespace SomeNamespace
{
    public class ArrayEnumerator<T> : IEnumerator<T>
    {
        public ArrayEnumerator(T[] arr)
        {
            collection = arr;
            length = arr.Length;
        }
        private readonly T[] collection;
        private int index = -1;
        private readonly int length;

        public T Current { get { return collection[index]; } }

        object IEnumerator.Current { get { return Current; } }

        public bool MoveNext() { index++; return index < length; }

        public void Reset() { index = -1; }

        public void Dispose() {/* Nothing to dispose. */}
    }
}
使用系统集合;
使用System.Collections.Generic;
名称空间名称空间
{
公共类数组分子:IEnumerator
{
公共阵列分子(T[]arr)
{
收集=arr;
长度=棱长;
}
私有只读T[]集合;
私有整数指数=-1;
私有只读整数长度;
公共T当前{get{return collection[index];}
对象IEnumerator.Current{get{return Current;}}
public bool MoveNext(){index++;返回索引

这或多或少等于Glenn Slayden提到的SzGenericarArrayeNumerator的.NET实现。当然,你只应该这样做,这是值得努力的情况。大多数情况下都不是这样。

LINQy代码实际上为Cast方法的结果返回了一个枚举数,而不是数组的枚举数……Guffa:由于枚举数只提供只读访问,所以在用法上没有太大区别。正如@Mehrdad所暗示的,使用
LINQ/Cast
具有完全不同的运行时行为,因为数组的每个元素都将通过一组额外的
MoveNext
Current
枚举器往返,如果数组很大,这可能会影响性能。在任何情况下,通过首先获得正确的枚举数(即使用我的答案中所示的两种方法之一),可以完全轻松地避免该问题。第一种方法是更好的选择!不要让任何人被完全多余的“奇特的LINQy”版本所欺骗。@GlennSlayden它不仅对大型阵列速度慢,而且对任何大小的阵列速度都慢。因此,如果在最内部的循环中使用
myArray.Cast().GetEnumerator()
,即使是很小的数组,也可能会显著降低速度。在使用
of type()
时,必须显式指定类型-至少在我的
double[]
GlennSlayden和Mehrdadadafshari所示的强制转换也是编译时的证明。@t3chb0t不,它们不是。这项工作是在运行时进行的,因为我们知道
Foo[]
实现了
IEnumerable
,但是如果这一点发生了变化,它将不会在编译时被检测到。显式强制转换从来都不是编译时的证明。相反,将数组赋值/返回为IEnumerable使用隐式转换,这是编译时的证明。@除非您试图转换的类型实现IEnumerable,否则对IEnumerable的显式转换不会编译。当你说“但如果它发生了变化”,你能提供一个例子来说明你的意思吗?当然,它是编译的。这就是为什么。编译器可能会警告您,某个强制转换不起作用。尝试
var foo=(int)new object()
。它编译得很好,并且在运行时崩溃。你能解释一下上面的代码会执行什么吗?这应该要高得多!使用编译器的隐式强制转换是最干净、最简单的解决方案。AsEnumerable()也执行强制转换,因此您仍在执行强制转换:)也许您可以使用您的_数组。OfType().GetEnumerator();区别在于它是在编译时完成的隐式转换,而不是在运行时完成的显式转换。因此,如果类型错误,则会出现编译时错误而不是运行时错误。@Hankshultz如果类型错误,则
您的数组。AsEnumerable()
不会首先编译,因为
AsEnumerable()
只能用于实现
IEnumerable
的类型的实例。出于好奇,为什么要获取枚举数?@Backwards\u Dave在我的例子中,在单线程环境中,有一个文件列表
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>
}
    public static IEnumerable<T> GetEnumerable<T>(this T[] arr)
    {
        return arr;
    }
    String[] arr = new String[0];
    arr.GetEnumerable().GetEnumerator()
    MyType[] arr = { new MyType(), new MyType(), new MyType() };

    IEnumerable<MyType> enumerable = arr;

    IEnumerator<MyType> en = enumerable.GetEnumerator();

    foreach (MyType item in enumerable)
    {

    }
using System.Collections;
using System.Collections.Generic;

namespace SomeNamespace
{
    public class ArrayEnumerator<T> : IEnumerator<T>
    {
        public ArrayEnumerator(T[] arr)
        {
            collection = arr;
            length = arr.Length;
        }
        private readonly T[] collection;
        private int index = -1;
        private readonly int length;

        public T Current { get { return collection[index]; } }

        object IEnumerator.Current { get { return Current; } }

        public bool MoveNext() { index++; return index < length; }

        public void Reset() { index = -1; }

        public void Dispose() {/* Nothing to dispose. */}
    }
}