C# 从数组中获取泛型枚举数
在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
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. */}
}
}