Laden...

Wann setze ich IEnumerable und yield ein?

Erstellt von JimStark vor 3 Jahren Letzter Beitrag vor 3 Jahren 729 Views
JimStark Themenstarter:in
309 Beiträge seit 2020
vor 3 Jahren
Wann setze ich IEnumerable und yield ein?

Hi,

mal eine ganz grundlegende Frage:

Angenommen ich hätte eine Klasse die auf eine Datenbank zugreift:


// Pseudocode:
public class Datenbank
{
     public IEnumerable GetArticles()
     {
        foreach...
          yield return article;
      }
      // oder
      public List<Article> GetArticles()
      {
            return articles;
       }
}

Gibt es da irgendwelche Performancevorteile, bzw. wann sollte ich auf IEnumerable zurückgreifen anstatt z.B. der ganzen Liste? Wenn ich sehr speicherintensive Objekte habe?

16.827 Beiträge seit 2008
vor 3 Jahren

Du hast bei Deiner Google Recherche nichts gefunden? 😉
Google-Suche nach when to use yield

Der Sinn von yield ist zB, dass Du den a) die Enumeration nicht unnötig erzeugst und damit b) evtl. den Speicher damit extrem vollhaust.
Im Endeffekt nutzt Du yield nur, wenn Du auch eine IEnumerable zurück gibst; was im .NET Leben nicht soooo oft vor kommt.

Bei Datenbanken macht das absolut keinen Sinn, weil Du normalerweise niemals diese Speichermengen lädst. Man lädt _eigentlich _fast niemals Daten ohne Skip und Take.
Im Falle von Datenbanken ist yield sogar eher schädlich, weil korrekt implementiert würde yield dazu führen, dass Du zwar eine Menge von Daten selektierst, durch das yield aber immer einen Single Query abfeuern würdest -> sinkende Performance.
Steht meines Wissens auch als Pitfall in der ADO.NET/EFCore Dokumentation.

foreach(var item in query)
{
   // Feuert jedes Mal einen Query gegen das einzelne Element an dieser Stelle
}

foreach(var item in query.ToList())  // Feuert einen einzigen Query, lädt alle Treffer in die Liste
{
  
}

Daher gibt eine Datenbankschnittstelle (zB das Repository) auch immer(meistens) ein IList zurück, weil zum Zeitpunkt der Rückgabe die Materialisierung schon erfolgen sollte.

Wenn man Daten als Menge selektiert, sollte man immer mit ToList arbeiten, um ein Materialisieren zu erzwingen.
Intermediate Materialization (C#)

JimStark Themenstarter:in
309 Beiträge seit 2020
vor 3 Jahren

Okay, das macht Sinn, danke.

Angenommen ich hätte einen Array mit Dateinamen von Bildern und ich will die geladenen Image Objekte z.B. mit GetAllImages() zurückgeben, bei sowas würde ich es dann einsetzen?


...
public IEnumerable GetAllImages()
{
...
   yield return Image.FromFile(image[i]);;
}


16.827 Beiträge seit 2008
vor 3 Jahren

Das kommt auf die Quelle Deiner Bilder an.

Wenn die Bilder aus einer Datenbank* oder vom Dateisystem (also jede IO-relevante Quelle) kommen, dann würde man ein IAsyncEnunumerable verwenden und nicht einzelne Einträge zurück geben; und dann evtl noch in Chunks; also einzelne Arrays.

Praxis:
Der Konsumen Deines Codes würde einfach nur eine Liste von Bild bekommen.
Im Hintergrund lädst Du aber nicht immer jedes Bild einzeln, sondern zB immer 100 auf einmal.
So verringerst Du den Overhead der Latenz des Queries; hast aber auch nicht immer alles im Speicher sondern maximal 100 Elemente.

Hier kannst Du sauber, performant und speicherschohnen mit yield arbeiten.
Die Schnittstille wäre am Ende IAsyncEnunumerable<IList<Bild>>

*ich kenne aktuell nur den Cosmos DB Treiber, der IAsyncEnumerable vollständig unterstützt.

A
764 Beiträge seit 2007
vor 3 Jahren

Hallo JimStark

Das mit den Bildern ist eigentlich ein gutes Beispiel. Angenommen du willst eine Vorschau mit maximal 10 Bildern anzeigen, aber nicht alle 4000 Bilder gleichzeitig laden, dann kannst du mit yield nur jeweils die benötigten Bilder für die Vorschau laden.

Dein Code ist nicht ganz korrekt.


public IEnumerable GetAllImages(IEnumerable<string> imagePathes)
{
  foreach (var imagePath in imagePathes)
  {
       yield return Image.FromFile(imagePath]);
  }
}

Edit: siehe auch Abts Kommentar.

Gruß
Alf

JimStark Themenstarter:in
309 Beiträge seit 2020
vor 3 Jahren

Super, vielen Dank euch Beiden!

5.941 Beiträge seit 2005
vor 3 Jahren

Hi Jim

Hier noch ein wenig Grundlagenwissen dazu:

@Abt
Der Tipp bezüglich des Materialisierens einer Menge ist wichtig 😃

Grüsse

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011