Vra

Dit is in C #, ek het 'n klas wat ek gebruik van DLL sommige anders se. Dit maak nie implementeer IEnumerable maar het 2 metodes wat slaag terug 'n IEnumerator. Is daar 'n manier wat ek kan 'n foreach lus op hierdie gebruik. Die klas ek gebruik is verseël.

Was dit nuttig?

Oplossing

foreach doen nie vereis IEnumerable, in teenstelling met die algemene opvatting. Al wat dit verg is 'n metode GetEnumerator dat enige voorwerp wat die metode MoveNext en die get-eiendom Current met die toepaslike handtekeninge het terug.

/ EDIT: In jou geval, egter, is jy uit van geluk. Jy kan trivially draai jou doel is egter om dit te laat 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: Die volgende metode, deur 'n paar mense voorgestel, doen nie werk as die metode terugkeer 'n IEnumerator:

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

Ander wenke

Re: As foreach n eksplisiete koppelvlak kontrak vereis nie, beteken dit vind GetEnumerator behulp weerspieëling?

(Ek kan nie kommentaar lewer nie, want ek het nie 'n hoë genoeg reputasie het.)

As jy impliseer runtime weerspieëling dan geen. Dit doen dit alles compiletime, ander minder bekende feit is dat dit ook kyk om te sien of die teruggekeer voorwerp wat kan Implementeer IEnumerator is besteebare.

Om dit te sien in aksie te oorweeg hierdie (uitvoerbare) brokkie.


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

Volgens MSDN :

foreach (type identifier in expression) statement

waar uitdrukking is:

  

Object versameling of skikking uitdrukking.   Die tipe van die versameling element   moet omgeskakel kan word na die identifiseerder wees   tik. Moenie 'n uitdrukking nie gebruik dat   evalueer om nul. Evalueer om 'n tipe   wat implemente IEnumerable of 'n tipe   wat verklaar 'n GetEnumerator metode.   In die laasgenoemde geval, GetEnumerator   moet óf weer 'n tipe wat   implemente IEnumerator of verklaar al   die metodes gedefinieer in IEnumerator.

Kort antwoord:

Jy moet 'n klas met 'n metode met die naam GetEnumerator , wat die IEnumerator jy reeds terug. Bereik met 'n eenvoudige wrapper:

class ForeachWrapper
{
  private IEnumerator _enumerator;

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

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

Gebruik:

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

Van die C # taal spesifikasie :

  

Die Stel-time verwerking van 'n   foreach verklaring eerste bepaal die   tipe versameling, tipe opnemer en   element tipe van die uitdrukking. hierdie   bepaling verloop soos volg:

     
      
  • As die tipe X van uitdrukking is 'n verskeidenheid tipe dan is daar 'n implisiete   verwysing omskakeling van X na die   System.Collections.IEnumerable   koppelvlak (sedert System.Array   implemente hierdie koppelvlak). Die   versameling tipe is die   System.Collections.IEnumerable   koppelvlak, die opnemer tipe is die   System.Collections.IEnumerator   koppelvlak en die tipe element is die   element tipe van die tipe reeks X.

  •   
  • Andersins, vas te stel of die tipe X het 'n toepaslike   GetEnumerator metode:

         
        
    • Voer lid lookup van die tipe X met identifiseerder GetEnumerator en geen   tik argumente. As die lid lookup   produseer nie 'n wedstryd, of dit   produseer 'n dubbelsinnigheid, of lewer 'n   wedstryd wat nie 'n metode groep,   kyk vir 'n enumerable koppelvlak as   hieronder beskryf. Dit word aanbeveel   dat 'n waarskuwing uitgereik word indien lid   lookup produseer enigiets behalwe 'n   metode groep of nie opgewasse.

    •   
    • Voer oorlading resolusie met behulp van die gevolglike metode groep en 'n   leë argument lys. As oorlading   resolusie resultate in geen van toepassing   metodes, resultate in 'n dubbelsinnigheid, of   resultate in 'n enkele beste metode maar   hierdie metode is óf statiese of nie   openbare, kyk vir 'n enumerable   koppelvlak soos hieronder beskryf. dit is   aanbeveel dat 'n waarskuwing uitgereik   As oorlading resolusie produseer   enigiets behalwe 'n ondubbelsinnige openbare   byvoorbeeld metode of geen van toepassing   metodes.

    •   
    • As die tipe terugkeer E van die GetEnumerator metode is nie 'n klas,   struct of koppelvlak tipe, 'n fout is   geproduseer en geen verdere stappe is   geneem.

    •   
    • Lid lookup uitgevoer op E met die identifikasie Huidige en geen   tik argumente. As die lid lookup   produseer nie opgewasse, die resultaat is 'n   Fout, of die gevolg is iets   behalwe 'n openbare instansie eiendom wat   toelaat lees, is 'n fout wat   en geen verdere stappe geneem word.

    •   
    • Lid lookup uitgevoer op E met die identifikasie Move Next en geen   tik argumente. As die lid lookup   produseer nie opgewasse, die resultaat is 'n   Fout, of die gevolg is iets   behalwe 'n metode groep, 'n fout is   geproduseer en geen verdere stappe is   geneem.

    •   
    • resolusie Overload is uitgevoer op die metode groep met 'n leë   argument lys. As oorlading resolusie   resultate in geen toepaslike metodes,   resultate in 'n dubbelsinnigheid of resultate in   'n enkele beste metode, maar hierdie metode   is óf statiese of nie publiek, of sy   terugkeer tipe is nie Bool, 'n fout is   geproduseer en geen verdere stappe is   geneem.

    •   
    • Die tipe versameling is X, die opnemer tipe is E, en die element   tipe is die tipe van die huidige   eiendom.

    •   
  •   
  • Andersins, gaan vir 'n enumerable koppelvlak:

         
        
    • As daar presies een tipe T so dat daar 'n implisiete   omskakeling van X na die koppelvlak   System.Collections.Generic.IEnumerable ,   dan die tipe versameling is hierdie   koppelvlak, die opnemer tipe is die   koppelvlak   System.Collections.Generic.IEnumerator ,   en die tipe element is T.

    •   
    • Andersins, indien daar meer as een so 'n tipe T, dan 'n fout is   geproduseer en geen verdere stappe is   geneem.

    •   
    • Andersins, as daar is 'n implisiete omskakeling van X na die   System.Collections.IEnumerable   koppelvlak, dan is die tipe versameling is   hierdie koppelvlak, die opnemer tipe is   die koppelvlak   System.Collections.IEnumerator, en   die tipe element is voorwerp.

    •   
    • Andersins, is 'n fout geproduseer en geen verdere stappe geneem word.

    •   
  •   

Nie streng. Solank as wat die klas het die vereiste GetEnumerator, Move Next, Herstel en huidige lede, sal dit werk met foreach

Nee, doen jy nie en jy hoef nie eens 'n GetEnumerator metode, Bv nodig:.

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

wat op hierdie manier word genoem:

Counter cnt = new Counter();

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

Jy kan altyd draai dit, en as 'n eenkant te wees "foreachable" jy moet net 'n metode met die naam "GetEnumerator" met die behoorlike handtekening hê.


class EnumerableAdapter
{
  ExternalSillyClass _target;

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

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

}

Gegewe die klas X met metodes A en B wat beide terugkeer IEnumerable, kan jy 'n foreach gebruik op die klas soos volg:

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

// or

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

Vermoedelik die betekenis vir die enumerables teruggekeer deur A en B is goed gedefinieer word.

@Brian: Nie seker jy probeer om lus oor die waarde terugkeer uit metode oproep of die klas self, As dit wat jy wil hê, is die klas dan deur maak dit 'n skikking wat jy kan gebruik met foreach.

Vir 'n klas aan bruikbare met foeach al wat dit nodig het om te doen is 'n openbare metode wat terugkeer en IEnumerator vernoem GetEnumerator (), dit is dit:

Neem die volgende klas, beteken dit nie implementeer IEnumerable of IEnumerator:

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

alternatiewelik die GetEnumerator () metode kan geskryf word:

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

Wanneer dit gebruik word in 'n foreach (Let daarop dat die geen omhulsel gebruik word, net 'n klas byvoorbeeld):

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

afdrukke:

  

1 2 3 4 5 6

Die tipe vereis slegs 'n openbare / nie-statiese / nie-generiese / parameterless metode met die naam GetEnumerator wat iets wat 'n openbare MoveNext metode en 'n openbare Current eiendom het sou terugkeer hê. Soos ek onthou mnr Eric Lippert iewers, hierdie is ontwerp ten einde pre generiese era vir beide tipe veiligheid en boks verwante prestasie kwessies in geval van tipes waarde te akkommodeer.

Byvoorbeeld dit werk:

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
{

}

Dit is dan vertaal deur die samesteller na:

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

Van 8.8.4 afdeling van die spec .


Iets opmerklik is die opnemer voorrang betrokke - dit gaan soos as jy 'n public GetEnumerator metode, dan is dit die verstek keuse van foreach ongeag wat die uitvoering daarvan. Byvoorbeeld:

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

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

    }
}

( As jy nie 'n openbare uitvoering (dws net eksplisiete implementering) het, dan voorrang gaan soos IEnumerator<T>> IEnumerator. )

Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top