C# Class 是否需要实现 IEnumerable 才能使用 Foreach

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/127233/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 14:46:49  来源:igfitidea点击:

Does Class need to implement IEnumerable to use Foreach

提问by Brian G

This is in C#, I have a class that I am using from some else's DLL. It does not implement IEnumerable but has 2 methods that pass back a IEnumerator. Is there a way I can use a foreach loop on these. The class I am using is sealed.

这是在 C# 中,我有一个类,我正在使用其他人的 DLL。它没有实现 IEnumerable,但有 2 个方法可以传回 IEnumerator。有没有办法可以在这些上使用 foreach 循环。我正在使用的类是密封的。

采纳答案by Konrad Rudolph

foreachdoes notrequire IEnumerable, contrary to popular belief. All it requires is a method GetEnumeratorthat returns any object that has the method MoveNextand the get-property Currentwith the appropriate signatures.

foreach没有要求IEnumerable,出乎人们意料。它所需要的只是一个方法GetEnumerator,该方法返回具有该方法MoveNextCurrent具有适当签名的 get-property 的任何对象。

/EDIT: In your case, however, you're out of luck. You can trivially wrap your object, however, to make it enumerable:

/编辑:但是,就您而言,您运气不佳。但是,您可以简单地包装您的对象以使其可枚举:

class EnumerableWrapper {
    private readonly TheObjectType obj;

    public EnumerableWrapper(TheObjectType obj) {
        this.obj = obj;
    }

    public IEnumerator<YourType> GetEnumerator() {
        return obj.TheMethodReturningTheIEnumerator();
    }
}

// Called like this:

foreach (var xyz in new EnumerableWrapper(yourObj))
    …;

/EDIT: The following method, proposed by several people, does notwork if the method returns an IEnumerator:

/编辑:下面的方法,由几个人提出的,确实没有工作,如果该方法返回一个IEnumerator

foreach (var yz in yourObj.MethodA())
    …;

回答by Torbj?rn Gyllebring

You could always wrap it, and as an aside to be "foreachable" you only need to have a method called "GetEnumerator" with the proper signature.

你总是可以包装它,作为“foreachable”的旁白,你只需要一个名为“GetEnumerator”的具有正确签名的方法。


class EnumerableAdapter
{
  ExternalSillyClass _target;

  public EnumerableAdapter(ExternalSillyClass target)
  {
    _target = target;
  }

  public IEnumerable GetEnumerator(){ return _target.SomeMethodThatGivesAnEnumerator(); }

}

回答by Bill the Lizard

Not strictly. As long as the class has the required GetEnumerator, MoveNext, Reset, and Current members, it will work with foreach

不严格。只要该类具有所需的 GetEnumerator、MoveNext、Reset 和 Current 成员,它就可以与 foreach 一起使用

回答by Joel Coehoorn

Given class X with methods A and B that both return IEnumerable, you could use a foreach on the class like this:

给定具有方法 A 和 B 都返回 IEnumerable 的 X 类,您可以像这样在类上使用 foreach:

foreach (object y in X.A())
{
    //...
}

// or

foreach (object y in X.B())
{
   //...
}

Presumably the meaning for the enumerables returned by A and B are well-defined.

大概 A 和 B 返回的可枚举的含义是明确定义的。

回答by Paul van Brenk

No, you don't and you don't even need an GetEnumerator method, e.g.:

不,您不需要,甚至不需要 GetEnumerator 方法,例如:

class Counter
{
    public IEnumerable<int> Count(int max)
    {
        int i = 0;
        while (i <= max)
        {
            yield return i;
            i++;
        }
        yield break;
    }
}

which is called this way:

这种方式称为:

Counter cnt = new Counter();

foreach (var i in cnt.Count(6))
{
    Console.WriteLine(i);
}

回答by Adam Hughes

According to MSDN:

根据MSDN

foreach (type identifier in expression) statement

where expression is:

其中表达式是:

Object collection or array expression. The type of the collection element must be convertible to the identifier type. Do not use an expression that evaluates to null. Evaluates to a type that implements IEnumerable or a type that declares a GetEnumerator method. In the latter case, GetEnumerator should either return a type that implements IEnumerator or declares all the methods defined in IEnumerator.

对象集合或数组表达式。集合元素的类型必须可转换为标识符类型。不要使用计算结果为 null 的表达式。计算为实现 IEnumerable 的类型或声明 GetEnumerator 方法的类型。在后一种情况下,GetEnumerator 应返回实现 IEnumerator 的类型或声明 IEnumerator 中定义的所有方法。

回答by Torbj?rn Gyllebring

Re: If foreach doesn't require an explicit interface contract, does it find GetEnumerator using reflection?

回复:如果 foreach 不需要显式接口契约,它会使用反射找到 GetEnumerator 吗?

(I can't comment since I don't have a high enough reputation.)

(我不能发表评论,因为我没有足够高的声誉。)

If you're implying runtimereflection then no. It does it all compiletime, another lesser known fact is that it also check to see if the returned object that mightImplement IEnumerator is disposable.

如果您指的是运行时反射,则不。它在编译时完成所有工作,另一个鲜为人知的事实是它还检查返回的可能实现 IEnumerator 的对象是否是一次性的。

To see this in action consider this (runnable) snippet.

要查看此操作,请考虑此(可运行)代码段。


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

namespace ConsoleApplication3
{
    class FakeIterator
    {
        int _count;

        public FakeIterator(int count)
        {
            _count = count;
        }
        public string Current { get { return "Hello World!"; } }
        public bool MoveNext()
        {
            if(_count-- > 0)
                return true;
            return false;
        }
    }

    class FakeCollection
    {
        public FakeIterator GetEnumerator() { return new FakeIterator(3); }
    }

    class Program
    {
        static void Main(string[] args)
        {
            foreach (string value in new FakeCollection())
                Console.WriteLine(value);
        }
    }
}

回答by VVS

Short answer:

简短的回答:

You need a class with a method named GetEnumerator, which returns the IEnumerator you already have. Achieve this with a simple wrapper:

您需要一个具有名为GetEnumerator方法的类,该类返回您已有的 IEnumerator。用一个简单的包装器实现这一点:

class ForeachWrapper
{
  private IEnumerator _enumerator;

  public ForeachWrapper(Func<IEnumerator> enumerator)
  {
    _enumerator = enumerator;
  }

  public IEnumerator GetEnumerator()
  {
    return _enumerator();
  }
}

Usage:

用法:

foreach (var element in new ForeachWrapper(x => myClass.MyEnumerator()))
{
  ...
}

From the C# Language Specification:

来自C# 语言规范

The compile-time processing of a foreach statement first determines the collection type, enumerator type and element type of the expression. This determination proceeds as follows:

  • If the type X of expression is an array type then there is an implicit reference conversion from X to the System.Collections.IEnumerable interface (since System.Array implements this interface). The collection type is the System.Collections.IEnumerable interface, the enumerator type is the System.Collections.IEnumerator interface and the element type is the element type of the array type X.

  • Otherwise, determine whether the type X has an appropriate GetEnumerator method:

    • Perform member lookup on the type X with identifier GetEnumerator and no type arguments. If the member lookup does not produce a match, or it produces an ambiguity, or produces a match that is not a method group, check for an enumerable interface as described below. It is recommended that a warning be issued if member lookup produces anything except a method group or no match.

    • Perform overload resolution using the resulting method group and an empty argument list. If overload resolution results in no applicable methods, results in an ambiguity, or results in a single best method but that method is either static or not public, check for an enumerable interface as described below. It is recommended that a warning be issued if overload resolution produces anything except an unambiguous public instance method or no applicable methods.

    • If the return type E of the GetEnumerator method is not a class, struct or interface type, an error is produced and no further steps are taken.

    • Member lookup is performed on E with the identifier Current and no type arguments. If the member lookup produces no match, the result is an error, or the result is anything except a public instance property that permits reading, an error is produced and no further steps are taken.

    • Member lookup is performed on E with the identifier MoveNext and no type arguments. If the member lookup produces no match, the result is an error, or the result is anything except a method group, an error is produced and no further steps are taken.

    • Overload resolution is performed on the method group with an empty argument list. If overload resolution results in no applicable methods, results in an ambiguity, or results in a single best method but that method is either static or not public, or its return type is not bool, an error is produced and no further steps are taken.

    • The collection type is X, the enumerator type is E, and the element type is the type of the Current property.

  • Otherwise, check for an enumerable interface:

    • If there is exactly one type T such that there is an implicit conversion from X to the interface System.Collections.Generic.IEnumerable<T>, then the collection type is this interface, the enumerator type is the interface System.Collections.Generic.IEnumerator<T>, and the element type is T.

    • Otherwise, if there is more than one such type T, then an error is produced and no further steps are taken.

    • Otherwise, if there is an implicit conversion from X to the System.Collections.IEnumerable interface, then the collection type is this interface, the enumerator type is the interface System.Collections.IEnumerator, and the element type is object.

    • Otherwise, an error is produced and no further steps are taken.

foreach 语句的编译时处理首先确定表达式的集合类型、枚举类型和元素类型。该确定过程如下:

  • 如果表达式的类型 X 是数组类型,则存在从 X 到 System.Collections.IEnumerable 接口的隐式引用转换(因为 System.Array 实现了此接口)。集合类型为 System.Collections.IEnumerable 接口,枚举器类型为 System.Collections.IEnumerator 接口,元素类型为数组类型 X 的元素类型。

  • 否则,确定类型 X 是否具有适当的 GetEnumerator 方法:

    • 使用标识符 GetEnumerator 对类型 X 执行成员查找,没有类型参数。如果成员查找没有产生匹配,或者产生歧义,或者产生不是方法组的匹配,请检查可枚举接口,如下所述。如果成员查找产生除方法组以外的任何内容或不匹配,建议发出警告。

    • 使用结果方法组和空参数列表执行重载决议。如果重载决议导致没有适用的方法、导致歧义或导致单个最佳方法但该方法是静态的或非公共的,请检查可枚举接口,如下所述。如果重载解析产生任何内容,除了明确的公共实例方法或没有适用的方法,建议发出警告。

    • 如果 GetEnumerator 方法的返回类型 E 不是类、结构或接口类型,则会产生错误并且不采取进一步步骤。

    • 成员查找是在 E 上执行的,标识符为 Current,没有类型参数。如果成员查找没有产生匹配,结果是错误,或者结果是除允许读取的公共实例属性之外的任何内容,则会产生错误并且不采取进一步的步骤。

    • 成员查找是在 E 上使用标识符 MoveNext 执行的,没有类型参数。如果成员查找没有产生匹配,结果是错误,或者结果是除方法组之外的任何内容,则会产生错误并且不采取进一步的步骤。

    • 重载解析是在具有空参数列表的方法组上执行的。如果重载决议导致没有适用的方法、导致歧义,或者导致只有一个最佳方法但该方法是静态的或非公共的,或者其返回类型不是 bool,则会产生错误并且不采取进一步的步骤。

    • 集合类型为 X,枚举数类型为 E,元素类型为 Current 属性的类型。

  • 否则,检查可枚举接口:

    • 如果只有一种类型 T 使得从 X 到接口 System.Collections.Generic.IEnumerable<T> 的隐式转换,则集合类型是这个接口,枚举类型是接口 System.Collections.Generic。 IEnumerator<T>,元素类型为 T。

    • 否则,如果存在多个这样的类型 T,则会产生错误并且不采取进一步的步骤。

    • 否则,如果存在从 X 到 System.Collections.IEnumerable 接口的隐式转换,则集合类型为该接口,枚举器类型为接口 System.Collections.IEnumerator,元素类型为 object。

    • 否则,将产生错误并且不采取进一步的步骤。

回答by c.sokun

@Brian: Not sure you try to loop over the value return from method call or the class itself, If what you want is the class then by make it an array you can use with foreach.

@Brian:不确定您是否尝试遍历从方法调用或类本身返回的值,如果您想要的是类,则将其设为可与 foreach 一起使用的数组。

回答by Pop Catalin

For a class to be usable with foeach all it needs to do is have a public method that returns and IEnumerator named GetEnumerator(), that's it:

对于一个可以与 foeach 一起使用的类,它需要做的就是有一个返回的公共方法和名为 GetEnumerator() 的 IEnumerator,就是这样:

Take the following class, it doesn't implement IEnumerable or IEnumerator :

采用以下类,它没有实现 IEnumerable 或 IEnumerator :

public class Foo
{
    private int[] _someInts = { 1, 2, 3, 4, 5, 6 };
    public IEnumerator GetEnumerator()
    {
        foreach (var item in _someInts)
        {
            yield return item;
        }
    }
}

alternatively the GetEnumerator() method could be written:

或者,可以编写 GetEnumerator() 方法:

    public IEnumerator GetEnumerator()
    {
        return _someInts.GetEnumerator();
    }

When used in a foreach ( Note that the no wrapper is used, just a class instance ):

在 foreach 中使用时(注意没有使用包装器,只是一个类实例):

    foreach (int item in new Foo())
    {
        Console.Write("{0,2}",item);
    }

prints:

印刷:

1 2 3 4 5 6

1 2 3 4 5 6