Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[c#] yield a single value

Pagina: 1
Acties:

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

Topicstarter
Ik heb een functie welke een IEnumerable<T> interface terug geeft. De functie doorloopt een vaste lijst en voegt random een item uit aan andere lijst toe aan het totale resultaat.

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class MyList
{
  private List<int> numbers = new List<int>();
  public MyList()
  {
    //populate list with some integers
    for(int i = 1; i <= 10; i++)
      this.numbers.Add(i);
  }

  public IEnumerable<int> GetItems(int extraNumber)
  {
    foreach(int i in this.numbers)
      yield return i;

    //return the extra integer
    do
    {
       yield return extraNumber;
    } while(false);
  }

  public static void Main(string[] args)
  {
    MyList list = new MyList();
    IEnumerable<int> result = list.GetItems(13);
    result = list.GetItems(28);
  }
}

De originele 'numbers' list is tevens read-only. De extra waarde toevoegen, enumerator terug geven, en daarna weer de waarde verwijderen is geen oplossing.

Hoewel de code perfect werkt, is mijn vraag of er misschien een nettere methode is om een enkele waarde als yield waarde terug te geven.

Volgens de documentatie over yield moet yield in een iterator body staan. Ik heb dus een 'loop' nodig welke slechts eenmalig uitgevoerd wordt.

[ Voor 0% gewijzigd door Niemand_Anders op 31-01-2008 13:54 . Reden: typo ]

If it isn't broken, fix it until it is..


  • Spiral
  • Registratie: December 2005
  • Niet online
C#:
1
2
3
4
5
6
public IEnumerable<int> GetItems(int extraNumber) 
  { 
    this.numbers.Add(extraNumber);
    foreach(int i in this.numbers) 
      yield return i; 
  } 

To say of what is that it is not, or of what is not that it is, is false, while to say of what is that it is, and of what is not that it is not, is true. | Aristoteles


  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

Topicstarter
Dat is niet mogelijk omdat numbers een readonly collectie is zoals ook aangegeven in de topic.

Daarnaast vergeet je daarbij de extra waarde weer te verwijderen zodat bij een tweede aanroep 11 ipv 10 items worden terug gegeven.

Het voorbeeld is vereenvoudigt om het probleem duidelijker neer te zetten. Zaken als locking en thead afhandeling zijn dus weggelaten omdat deze niets toevoegen aan de vraag zelf.

De functie moet dus altijd alleen de oorspronkelijke 9 nummers + het extra nummer terug geven. In totaal dus altijd 10 nummers in het geval van het voorbeeld.

In de praktijk wordt de lijst gebruikt om o.a. X.509 certificaten terug te geven en vervolgens gegevens over een FDN netwerk te communiceren. Er zijn dan meerdere MyList instanties waarbij elke lijst zijn eigen collectie 'statische' gegevens heeft. Vervolgens wordt uit de dynamische lijst 1 item extra toegevoegd aan de enumeratie. Dat extra item is in het voorbeeld als 'int extraNumber' opgenomen in de code. Communicatie op dit netwerk is altijd 1-op-1.

Zou je de resultaten (result) uit mijn code afdrukken dan krijg je de volgende twee lijstjes:
1 2 3 4 5 6 7 8 9 13
1 2 3 4 5 6 7 8 9 28

De vraag je dus of
C#:
1
2
3
4
5
6
7
8
9
10
public IEnumerate<int> GetItems(int number)
{
   //dit geeft een compile error
   // yield return number;
   
   do
   {
       yield return number;
   } while(false);
}

netter c.q. efficienter geschreven kan worden. Ik vind de work around namelijk super lelijk en ik vraag me of dat dat dus beter geschreven kan worden.

Omdat vrijwel alle code asynchroon wordt uitgevoerd is het gebruik van yielding erg belangrijk. De combinatie foreach, IEnumerator<T> en yield vormen samen een soort van stream. Dat maakt het mogelijk om items al te verwerken voordat de gehele collectie terug gegeven is (c.q. doorlopen in de enumerator).

If it isn't broken, fix it until it is..


  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Niemand_Anders schreef op donderdag 31 januari 2008 @ 15:33:
Dat is niet mogelijk omdat numbers een readonly collectie is zoals ook aangegeven in de topic.

Daarnaast vergeet je daarbij de extra waarde weer te verwijderen zodat bij een tweede aanroep 11 ipv 10 items worden terug gegeven.

Het voorbeeld is vereenvoudigt om het probleem duidelijker neer te zetten. Zaken als locking en thead afhandeling zijn dus weggelaten omdat deze niets toevoegen aan de vraag zelf.

De functie moet dus altijd alleen de oorspronkelijke 9 nummers + het extra nummer terug geven. In totaal dus altijd 10 nummers in het geval van het voorbeeld.

In de praktijk wordt de lijst gebruikt om o.a. X.509 certificaten terug te geven en vervolgens gegevens over een FDN netwerk te communiceren. Er zijn dan meerdere MyList instanties waarbij elke lijst zijn eigen collectie 'statische' gegevens heeft. Vervolgens wordt uit de dynamische lijst 1 item extra toegevoegd aan de enumeratie. Dat extra item is in het voorbeeld als 'int extraNumber' opgenomen in de code. Communicatie op dit netwerk is altijd 1-op-1.

Zou je de resultaten (result) uit mijn code afdrukken dan krijg je de volgende twee lijstjes:
1 2 3 4 5 6 7 8 9 13
1 2 3 4 5 6 7 8 9 28

De vraag je dus of
C#:
1
2
3
4
5
6
7
8
9
10
public IEnumerate<int> GetItems(int number)
{
   //dit geeft een compile error
   // yield return number;
   
   do
   {
       yield return number;
   } while(false);
}

netter c.q. efficienter geschreven kan worden. Ik vind de work around namelijk super lelijk en ik vraag me of dat dat dus beter geschreven kan worden.

Omdat vrijwel alle code asynchroon wordt uitgevoerd is het gebruik van yielding erg belangrijk. De combinatie foreach, IEnumerator<T> en yield vormen samen een soort van stream. Dat maakt het mogelijk om items al te verwerken voordat de gehele collectie terug gegeven is (c.q. doorlopen in de enumerator).
ik zou hem persoonlijk in een for loop zetten. Doet natuurlijk hetzelfde maar het is duidelijker dat je hem expres 1 keer uit wilt laten voeren.
C#:
1
2
3
4
for(int i=0; i < 1; i++ )
{
    yield return number;
}

Een andere manier zou ik ook niet weten.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • Xiphalon
  • Registratie: Juni 2001
  • Laatst online: 19-11 17:12
Niemand_Anders schreef op donderdag 31 januari 2008 @ 13:53:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class MyList
{
  private List<int> numbers = new List<int>();
  public MyList()
  {
    //populate list with some integers
    for(int i = 1; i <= 10; i++)
      this.numbers.Add(i);
  }

  public IEnumerable<int> GetItems(int extraNumber)
  {
    foreach(int i in this.numbers)
      yield return i;

    //return the extra integer
    do
    {
       yield return extraNumber;
    } while(false);
  }

  public static void Main(string[] args)
  {
    MyList list = new MyList();
    IEnumerable<int> result = list.GetItems(13);
    result = list.GetItems(28);
  }
}
Een iterator body is de body van een functie welke een IEnumerable retourneert.
C#:
1
2
3
4
5
6
7
        public IEnumerable<int> GetItems(int extraNumber)
        {
            foreach (int i in this.numbers)
                yield return i;

            yield return extraNumber;
        }

is gewoon correct, en werkt dan ook prima hier (VS 2008)

  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 31-10 11:58
Zo werkt het in elk geval ook:
C#:
1
2
3
4
5
6
7
8
public IEnumerable<int> GetItems(int extraNumber)
{
    List<int> t = new List<int>(numbers);
    t.Add(extraNumber);

    foreach (int i in t)
        yield return i;
}

Maar wat darkmage schreef werkt inderdaad ook :)

[ Voor 29% gewijzigd door riezebosch op 31-01-2008 16:24 ]

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-11 23:43

.oisyn

Moderator Devschuur®

Demotivational Speaker

Idd
The yield statement can only appear inside an iterator block, which might be used as a body of a method, operator, or accessor.
De body van een for-, foreach-, while- of do-statement heet geen iterator block :)

[ Voor 24% gewijzigd door .oisyn op 31-01-2008 16:31 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Malthus
  • Registratie: April 2003
  • Laatst online: 23-09 15:03
Niemand_Anders schreef op donderdag 31 januari 2008 @ 13:53:
Volgens de documentatie over yield moet yield in een iterator body staan. Ik heb dus een 'loop' nodig welke slechts eenmalig uitgevoerd wordt.
Volgens mij moet de yield in een iterator body staan omdat de yield anders geen functie heeft. Een enkele waarde kun je ook zonder yield teruggeven (lijkt mij, maar ik heb dit niet getest):

C#:
1
2
3
4
5
6
7
  public IEnumerable<int> GetItems(int extraNumber)
  {
    foreach(int i in this.numbers)
      yield return i;

    return extraNumber;
  }

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Malthus schreef op vrijdag 01 februari 2008 @ 20:21:
[...]


Volgens mij moet de yield in een iterator body staan omdat de yield anders geen functie heeft. Een enkele waarde kun je ook zonder yield teruggeven (lijkt mij, maar ik heb dit niet getest):

C#:
1
2
3
4
5
6
7
  public IEnumerable<int> GetItems(int extraNumber)
  {
    foreach(int i in this.numbers)
      yield return i;

    return extraNumber;
  }
Dat kan dus niet want je returnt nu een int terwijl het return type van de methode IEnumerable<int> is. De yield is extra syntax om te zorgen dat er een Iterator terug gegeven word.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

Topicstarter
De oplossing van Darkmage werkt. Gewoon yield return extraNumber. Je kunt niet gewoon 'return' aanroepen omdat de een IEnumerable wordt verwacht als return type en extraNumber in dit geval een int is. Yield kun je misschien het beste zien als een event van de enumerator welke wordt aangeroepen voor elke iteratie. Het is bijna alsof de code uit de for/foreach/while loop in GetItems staat. De CLR wisselt de focus tussen de loop en de enumerator. Zeer efficient voor grote collecties dus.

De oplossing van Riezenbosch kost helaas te veel resources. Deze methode gaat gebruikt worden in een project waarmee dan 1 miljoen transacties per dag worden afgehandeld. Onnodig integers kopieren naar een nieuwe lijst om daar vervolgens een item aan toe te voegen is dan niet handig.

Het is eigenlijk ongeloofelijk dat ik gewoon niet zelf heb geprobeert om yield return te gebruiken buiten een loop. Zal wel door het carnaval komen denk ik. Zit nu enkele weken op lokatie in Oeteldonk (Den Bosch) en dat is daar op kantoor om vrijdag ochtend met hoop bier begonnen. Toen ik vroeg om een kopje koffie, werd het stil en keek iedereen mijn aan. Toen heb ik op 10.00 's ochtend ook maar een biertje genomen en daarna nog een en nog een. Na mijn topic ben ik dus maar gestopt (met werken).

Benieuwd hoe 'helder' ik vandaag ben.

In ieder geval bedankt voor alle reacties.

If it isn't broken, fix it until it is..

Pagina: 1