Laden...

[erledigt] yield return element vs return list

Erstellt von Diräkt vor 12 Jahren Letzter Beitrag vor 12 Jahren 2.361 Views
D
Diräkt Themenstarter:in
615 Beiträge seit 2009
vor 12 Jahren
[erledigt] yield return element vs return list

Hallo Leute

Kann mir bitte jemand sagen was man verwenden sollte und vorallem wieso :

1. Yield return


public IEnumerable<Product> GetAllProducts()
{
    using (myEntities db = new myEntities ())
    {
        var products = from product in db.Product
                       select product;

        foreach (Product product in products)
        {
            yield return product;
        }
    }
}

2. Return List<T>


public IEnumerable<Product> GetAllProducts()
{
    using (myEntities db = new myEntities())
    {
        var products = from product in db.Product
                       select product;

        return products.ToList<Product>();
    }
}

Stimmt folgendes :
--> Yield gibt das resultat "nacheinander zurück" somit vorteilhaft (schneller) wenn das Resultat per Binding an das UI gebunden wird ?
(Yield macht nur Sinn wenn die Klasse INotifyPropertyChange implementiert ?!)

Danke für Eure Antworten um Licht in die Dunkelheit zu bringen 😃

Beste Grüsse

Diräkt

1.346 Beiträge seit 2008
vor 12 Jahren

Der unterschied zwichen den beiden Varianten ist, dass der ohne yield alle Ergebnisse beim ersten durchlaufen an der Stelle

return products.ToList<Product>();

gesammelt werden. Wenn man die Methode mit yield so verwendet:

var e = GetAllProducts();
foreach(var product in e){
...

wird in der Zeile GetAllProducts() die Methode bis zum ersten yield ausgeführt. Dann geht es weiter zum foreach. Dort wird das erste Ergebnis ausgelesen und die Methode läuft weiter bis zum nächsten usw..

Ich denke einen Performancevorteil bringt es nicht wirklich, bis vllt auf einen Speichergewinn bei einem großen Ergebnis bei dem man viele Ergebnisse aus der Datenbank lädt und dann verarbeitet (wenn man sie in kleinen stücken rauslädt. So bleibt nur ein kleiner Teil im Speicher, und es wird nicht sofort alles geladen.

Bei linq ist es ähnlich.

        var products = from product in db.Product
                       select product;

baut im Grunde erst das Statement zusammen. Erst wenn man da durch itteriert werden die Ergebnisse geladen.

1.130 Beiträge seit 2007
vor 12 Jahren

Yield gibt das resultat "nacheinander zurück" somit vorteilhaft (schneller) wenn das Resultat per Binding an das UI gebunden wird ?

Japp, yield baut eine state maschine. Z.B. in Yield, and the C# state machine hat jemand das mal genauer angeschaut.
In manchen Situationen sicherlich schneller, aber im Speziellem Falle muss das nicht unbedingt so sein.

Die Länge ist bei yield return nicht bekant, bevor der gesamte Enumerator durchlaufen wurde. Deshalb muss eventuell sowieso ne Liste erstellt werden oder der Enumerator wird mehrfach durchlaufen. Außerdem handelst du dir mit yield leichter Threading-Probleme ein.

Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!

771 Beiträge seit 2009
vor 12 Jahren

Hi Diräkt,

in deinem konkreten Fall sind aber deine beiden Varianten ungünstig.
Nimm einfach


public IEnumerable<Product> GetAllProducts()
{
    using (myEntities db = new myEntities ())
    {
        var products = from product in db.Product
                              select product;

        return products;
    }
}

Der Linq-Ausdruck liefert ja ein IQueryable<Product> zurück, und da IQueryable<T> von IEnumerable<T> abgeleitet ist, kannst du diesen Ausdruck "diräkt" 😁 so zurückgeben.

S
417 Beiträge seit 2008
vor 12 Jahren

Hallo Cat,

deine Variante ist problematisch, da Du den ObjectContext disposed und bei einer späteren Evaluierung (z.b. mit ToList) der Zugriff fehlschlägt (da db dann bereits disposed ist).
Siehe auch: ObjectContext instance has been disposed problem while binding

771 Beiträge seit 2009
vor 12 Jahren

Ups, daran habe ich gar nicht gedacht (ich habe nur das "using" so übernommen...)
Ok, aber normalerweise sollte dann die Entity-Instanz besser eine Membervariable der Klasse sein, also:


private myEntities db = new myEntities();

public IEnumerable<Product> GetAllProducts()
{
     var products = from product in db.Product
                           select product;

     return products;
}

D
Diräkt Themenstarter:in
615 Beiträge seit 2009
vor 12 Jahren

Besten Dank für Eure Antworten.

@Cat

Ok, aber normalerweise sollte dann die Entity-Instanz besser eine Membervariable der Klasse sein, also:

Dies wäre bei einem WebApp Projekt wohl nicht sehr sinvoll ?!

Beste Grüsse

Diräkt

5.742 Beiträge seit 2007
vor 12 Jahren

Hallo zusammen,

generell würde ich ein "LINQ-ToDB Ausdruck" aus den gegebenen Gründen nie direkt zurückgeben.
Auch, weil man nie genau wissen kann, ob mehrfaches Iterieren nicht am Ende mehrfach die DB abfrägt (wäre performancetechnisch fatal!).

Zur Variante mit dem yield return: Das ist vollkommen unsinnig und löst auch das Problem nicht.

yield return ist vor allem da sinnvoll, wo man selbst ein Enumerator bauen möchte z.B. (nehmen wir an, man hätte kein Enumerable.Range 😉 ):


public IEnumerable<int> GetNumbers(int upper)
{
  for (int i = 0; i < upper; i++)
    yield return i;
}

Statt ToList würde ich jedoch ToArray verwenden, wenn der Rückgabewert sowieso ein IEnumerable<T> ist - schreibend kann man ja sowieso nicht mehr darauf zugreifen; also:


public IEnumerable<Product> GetAllProducts()
{
    using (myEntities db = new myEntities())
        return db.Product.ToArray();
}