从数组中获取通用枚举器

发布于 2024-08-01 13:32:41 字数 348 浏览 7 评论 0原文

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

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

MyType[] MyArray = ... ;
IEnumerator<MyType> MyIEnumerator = MyArray.GetEnumerator() as IEnumerator<MyType>;

In C#, how does one obtain a generic enumerator from a given array?

In the code below, MyArray is an array of MyType objects. I'd like to obtain MyIEnumerator in the fashion shown,
but it seems that I obtain an empty enumerator (although I've confirmed that MyArray.Length > 0).

MyType[] MyArray = ... ;
IEnumerator<MyType> MyIEnumerator = MyArray.GetEnumerator() as IEnumerator<MyType>;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

朕就是辣么酷 2024-08-08 13:32:41

适用于 2.0+:

((IEnumerable<MyType>)myArray).GetEnumerator()

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

myArray.Cast<MyType>().GetEnumerator()   // returns IEnumerator<MyType>

Works on 2.0+:

((IEnumerable<MyType>)myArray).GetEnumerator()

Works on 3.5+ (fancy LINQy, a bit less efficient):

myArray.Cast<MyType>().GetEnumerator()   // returns IEnumerator<MyType>
轻许诺言 2024-08-08 13:32:41

您可以自己决定强制转换是否丑陋到足以保证进行无关的库调用:

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()
}

为了完整性,还应该注意以下内容是不正确的 - 并且会在运行时崩溃 - 因为 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>
}

神秘的是,为什么 SZGenericArrayEnumerator 不继承 SZArrayEnumerator(当前标记为“密封”的内部类),因为这将允许 (协变) 默认返回通用枚举器?

You can decide for yourself whether casting is ugly enough to warrant an extraneous library call:

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()
}

And for completeness, one should also note that the following is not correct--and will crash at runtime--because T[] chooses the non-generic IEnumerable interface for its default (i.e. non-explicit) implementation of 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>
}

The mystery is, why doesn't SZGenericArrayEnumerator<T> inherit from SZArrayEnumerator--an internal class which is currently marked 'sealed'--since this would allow the (covariant) generic enumerator to be returned by default?

夜唯美灬不弃 2024-08-08 13:32:41

由于我不喜欢选角,所以更新一下:

your_array.AsEnumerable().GetEnumerator();

Since I don't like casting, a little update:

your_array.AsEnumerable().GetEnumerator();
感悟人生的甜 2024-08-08 13:32:41

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

    public static IEnumerable<T> GetEnumerable<T>(this T[] arr)
    {
        return arr;
    }

// 使用代码:

    String[] arr = new String[0];
    arr.GetEnumerable().GetEnumerator()

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

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

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

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

To Make it as clean as possible I like to let the compiler do all of the work. There are no casts (so its actually type-safe). No third party Libraries (System.Linq) are used (No runtime overhead).

    public static IEnumerable<T> GetEnumerable<T>(this T[] arr)
    {
        return arr;
    }

// And to use the code:

    String[] arr = new String[0];
    arr.GetEnumerable().GetEnumerator()

This takes advantage of some compiler magic that keeps everything clean.

The other point to note is that my answer is the only answer that will do compile-time checking.

For any of the other solutions if the type of "arr" changes, then calling code will compile, and fail at runtime, resulting in a runtime bug.

My answer will cause the code to not compile and therefore I have less chance of shipping a bug in my code, as it would signal to me that I am using the wrong type.

凶凌 2024-08-08 13:32:41
    MyType[] arr = { new MyType(), new MyType(), new MyType() };

    IEnumerable<MyType> enumerable = arr;

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

    foreach (MyType item in enumerable)
    {

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

    IEnumerable<MyType> enumerable = arr;

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

    foreach (MyType item in enumerable)
    {

    }
掩于岁月 2024-08-08 13:32:41

YourArray.OfType().GetEnumerator();

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

YourArray.OfType<StringId>().GetEnumerator();

may perform a little better, since it only has to check the type, and not cast.

遇见了你 2024-08-08 13:32:41

当然,您可以做的就是为数组实现您自己的通用枚举器。

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. */}
    }
}

这或多或少等于 SZGenericArrayEnumerator的 .NET 实现。 正如格伦·斯莱登所提到的。 当然,只有在值得付出努力的情况下才应该这样做。 大多数情况下并非如此。

What you can do, of course, is just implement your own generic enumerator for arrays.

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. */}
    }
}

This is more or less equal to the .NET implemenation of SZGenericArrayEnumerator<T> as mentioned by Glenn Slayden. You should of course only do this, is cases where this is worth the effort. In most cases it is not.

余生共白头 2024-08-08 13:32:41

由于@Mehrdad Afshari 回答我为此创建了扩展方法:

public static IEnumerator<T> GetEnumerator<T>(this T[] array) {
    return (IEnumerator<T>)array.GetEnumerator();
}

所以你可以像这样实现 IEnumerable :

public class MulticoloredLine : IEnumerable<ColoredLine> {
    private ColoredLine[] coloredLines;    

    #region IEnumerable<ColoredLine>
    public IEnumerator<ColoredLine> GetEnumerator() => 
    coloredLines.GetEnumerator<ColoredLine>();
    IEnumerator IEnumerable.GetEnumerator() => coloredLines.GetEnumerator();
    #endregion
}

Since @Mehrdad Afshari answer i made extension method for that:

public static IEnumerator<T> GetEnumerator<T>(this T[] array) {
    return (IEnumerator<T>)array.GetEnumerator();
}

So you can implement IEnumerable like this:

public class MulticoloredLine : IEnumerable<ColoredLine> {
    private ColoredLine[] coloredLines;    

    #region IEnumerable<ColoredLine>
    public IEnumerator<ColoredLine> GetEnumerator() => 
    coloredLines.GetEnumerator<ColoredLine>();
    IEnumerator IEnumerable.GetEnumerator() => coloredLines.GetEnumerator();
    #endregion
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文