문제

이것은 C#에 있습니다. 다른 DLL에서 사용하는 클래스가 있습니다. ienumerable을 구현하지는 않지만 ienumerator를 뒤로 전달하는 2 가지 방법이 있습니다. 이것들에 Foreach 루프를 사용할 수있는 방법이 있습니까? 내가 사용하는 수업은 봉인되었습니다.

도움이 되었습니까?

해결책

foreach 하다 ~ 아니다 필요하다 IEnumerable, 대중의 믿음과는 반대로. 필요한 것은 방법입니다 GetEnumerator 메소드가있는 객체를 반환합니다 MoveNext 그리고 Get-Property Current 적절한 서명으로.

/편집 : 그러나 귀하의 경우에는 운이 좋지 않습니다. 그러나 사소하게 객체를 감싸서 열거 할 수 있도록 할 수 있습니다.

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))
    …;

/편집 : 여러 사람이 제안한 다음 방법은 ~ 아니다 메소드가 반환되는 경우 작동합니다 IEnumerator:

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

다른 팁

Re : Foreach가 명시 적 인터페이스 계약이 필요하지 않은 경우 반사를 사용하여 getEnumerator를 찾습니까?

(명성이 충분하지 않아서 언급 할 수 없습니다.)

당신이 암시한다면 실행 시간 그런 다음 반사 아니요. 그것은 모든 컴파일 타임을 수행하고, 또 다른 덜 알려진 사실은 반환 된 객체가 ~할 것 같다 구현 iEenumerator는 일회용입니다.

이것을 실제로 보려면 이것 (런 가능한) 스 니펫을 고려하십시오.


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

에 따르면 MSDN:

foreach (type identifier in expression) statement

여기서 표현은 :

객체 수집 또는 배열 표현식. 수집 요소의 유형은 식별자 유형으로 변환 할 수 있어야합니다. null로 평가하는 표현을 사용하지 마십시오. ienumerable을 구현하는 유형 또는 GetEnumerator 메소드를 선언하는 유형으로 평가합니다. 후자의 경우, getEnumerator는 ienumerator를 구현하는 유형을 반환하거나 ienumerator에 정의 된 모든 방법을 선언해야합니다.

짧은 대답:

이름이 지정된 메소드가있는 클래스가 필요합니다 getEnumerator, 이미 가지고있는 ienumerator를 반환합니다. 간단한 래퍼로이를 달성하십시오.

class ForeachWrapper
{
  private IEnumerator _enumerator;

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

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

용법:

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

로부터 C# 언어 사양:

Foreach 문의 컴파일 타임 처리는 먼저 수집 유형, 열거 자 유형 및 표현식의 요소 유형을 결정합니다. 이 결정은 다음과 같이 진행됩니다.

  • 표현식 X가 배열 유형 인 경우 x에서 System.Collections.ienumerable 인터페이스로 암시 적 참조 변환이 있습니다 (System.array 가이 인터페이스를 구현하므로). 수집 유형은 System.collections.ienumerable 인터페이스입니다. 열거기 유형은 System.collections.ienumerator 인터페이스이며 요소 유형은 배열 유형 X의 요소 유형입니다.

  • 그렇지 않으면 유형 X에 적절한 getEnumerator 메소드가 있는지 확인합니다.

    • 식별자 getEnumerator와 유형 인수가없는 유형 X에서 멤버 조회를 수행하십시오. 멤버 조회가 일치를 생성하지 않거나 모호성을 생성하거나 메소드 그룹이 아닌 일치를 생성하는 경우 아래에 설명 된대로 열거 가능한 인터페이스를 확인하십시오. 회원 조회가 메소드 그룹을 제외한 또는 일치하지 않는 경우 경고를 발행하는 것이 좋습니다.

    • 결과 메소드 그룹과 빈 인수 목록을 사용하여 과부하 해상도를 수행하십시오. 오버로드 해상도가 적용 가능한 방법이 없으면 모호성을 초래하거나 단일 최상의 방법을 초래하지만 해당 방법은 정적이거나 공개되지 않으므로 아래에 설명 된대로 열거 가능한 인터페이스를 확인하십시오. 오버로드 해상도가 명백한 공개 인스턴스 방법을 제외한 모든 것을 생성하거나 해당 방법이없는 경우 경고를 발행하는 것이 좋습니다.

    • getEnumerator 메소드의 반환 유형 E가 클래스, 구조 또는 인터페이스 유형이 아닌 경우 오류가 생성되고 더 이상 단계가 취하지 않습니다.

    • 멤버 조회는 식별자 전류 및 유형 인수가없는 E에서 수행됩니다. 멤버 조회가 일치하지 않으면 결과는 오류가 발생하거나 결과는 읽기를 허용하는 공개 인스턴스 속성을 제외하고는 오류가 생성되고 더 이상 단계를 수행하지 않습니다.

    • 멤버 조회는 식별자 movenext 및 유형 인수가없는 E에서 수행됩니다. 멤버 조회가 일치하지 않으면 결과는 오류가 발생하거나 결과는 메소드 그룹을 제외한 모든 것입니다. 오류가 생성되고 더 이상 단계를 수행하지 않습니다.

    • 오버로드 해상도는 빈 인수 목록이있는 메소드 그룹에서 수행됩니다. 오버로드 해상도가 적용 가능한 방법이 없으면 모호함을 유발하거나 단일 최상의 방법을 초래하지만 해당 방법은 정적이거나 공개되지 않거나 반환 유형이 부울이 아니며 오류가 생성되고 더 이상 단계를 수행하지 않습니다.

    • 수집 유형은 X이고, 열거기 유형은 E이고, 요소 유형은 현재 속성의 유형입니다.

  • 그렇지 않으면 열거 가능한 인터페이스를 확인하십시오.

    • x에서 인터페이스 시스템으로의 암시 적 변환이있는 유형 T가 정확히 하나가있는 경우 .collections.generic.ienumerableu003CT> , 그러면 수집 유형은이 인터페이스이며, 열거기 유형은 인터페이스 시스템입니다 .Collections.generic.ienumeratoru003CT> 및 요소 유형은 T입니다.

    • 그렇지 않으면, 이러한 유형 T가 둘 이상인 경우 오류가 생성되고 더 이상 단계가 취하지 않습니다.

    • 그렇지 않으면, x에서 system.collections.ienumerable 인터페이스로 암시 적 변환이있는 경우, 수집 유형은이 인터페이스이고, 열거기 유형은 인터페이스 시스템입니다. collections.ienumerator, 요소 유형은 객체입니다.

    • 그렇지 않으면 오류가 생성되고 더 이상 단계가 취하지 않습니다.

엄격하게. 클래스에 필요한 getEnumerator, Movenext, Reset 및 현재 멤버가있는 한 Foreach와 함께 작동합니다.

아니요, 당신은 그렇지 않으며 getEnumerator 방법이 필요하지 않습니다.

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

이 방법이라고합니다.

Counter cnt = new Counter();

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

당신은 항상 그것을 포장 할 수 있으며, "적절한"것을 따로 떼어 놓을 수 있습니다. 당신은 적절한 서명이있는 "getEnumerator"라는 메소드 만 있으면됩니다.


class EnumerableAdapter
{
  ExternalSillyClass _target;

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

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

}

Ienumerable을 모두 반환하는 메소드 A와 B가있는 클래스 X가 주어지면 다음과 같은 클래스에서 Foreach를 사용할 수 있습니다.

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

// or

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

아마도 A와 B에 의해 반환 된 열거 형제의 의미는 잘 정의되어 있습니다.

@Brian : 메소드 호출 또는 클래스 자체에서 값을 반환하는 것을 고치려고 노력하십시오. 원하는 것이 클래스라면 Foreach와 함께 사용할 수있는 배열로 만들어줍니다.

클래스가 foeach로 사용할 수있는 것은 getEnumerator ()라는 반환 및 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;
        }
    }
}

또는 GetEnumerator () 메소드를 작성할 수 있습니다.

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

Foreach에 사용될 때 (래퍼 없음이 사용되며 클래스 인스턴스 만 사용) :

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

인쇄물:

1 2 3 4 5 6

이 유형은 공개/비 정적/비 게 니체/매개 변수가없는 메소드 만 있으면됩니다. GetEnumerator 대중이있는 것을 반환해야합니다 MoveNext 방법과 대중 Current 재산. 어딘가에 Eric Lippert 씨를 회상하면서 이것은 값 유형의 경우 유형 안전 및 권투 관련 성능 문제에 대한 사전 일반 시대를 수용하도록 설계되었습니다.

예를 들어 이것은 작동합니다.

class Test
{
    public SomethingEnumerator GetEnumerator()
    {

    }
}

class SomethingEnumerator
{
    public Something Current //could return anything
    {
        get { }
    }

    public bool MoveNext()
    {

    }
}

//now you can call
foreach (Something thing in new Test()) //type safe
{

}

그런 다음 컴파일러에 의해 다음과 같이 번역됩니다.

var enumerator = new Test().GetEnumerator();
try {
   Something element; //pre C# 5
   while (enumerator.MoveNext()) {
      Something element; //post C# 5
      element = (Something)enumerator.Current; //the cast!
      statement;
   }
}
finally {
   IDisposable disposable = enumerator as System.IDisposable;
   if (disposable != null) disposable.Dispose();
}

사양의 8.8.4 섹션에서.


주목할만한 가치는 열거 자 우선 순위가 관련되어 있습니다. public GetEnumerator 방법은 기본 선택입니다 foreach 누가 그것을 구현하고 있는지에 관계없이. 예를 들어:

class Test : IEnumerable<int>
{
    public SomethingEnumerator GetEnumerator()
    {
        //this one is called
    }

    IEnumerator<int> IEnumerable<int>.GetEnumerator()
    {

    }
}

(공개 구현이없는 경우 (즉, 명시 적 구현 만) 우선 순위는 다음과 같습니다. IEnumerator<T> > IEnumerator.)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top