Mittendrin.

Zurück

Flurfunk der eXperts.

Hier erfahren Sie mehr über eXperts, Technologien und das wahre Leben in der SDX.

LINQ Coding Guidelines #11 – Der Einsatz von “var”

04.05.201508:00 Uhr , Alexander Jung

Es gibt Leute, die halten var für die Quelle des Bösen, andere haben eine ausgewogenere Haltung, der eher der Intention nahe kommt.

Empfehlung:
Bei LINQ-Ausdrücken ist der Einsatz von var zu empfehlen.

 

Man mag geteilter Ansicht darüber sein, ob es Sinn macht, einfache Datentypen wie int oder string durch var zu ersetzen. Aber sobald es um Templates geht rechtfertigt sich der Einsatz von var alleine durch den Schreibaufwand. Und spätestens im Zusammenhang mit LINQ wird type inference, also die implizite Typisierung, zum beherrschenden Aspekt.

Bei LINQ ergibt sich der Typ aus der Kette der Aufrufe – und er ändert sich potentiell mit jedem Select(). Dabei ist nicht nur der Datentyp der Elemente relevant – hier bekommt var durch anonyme Typen seine besondere Rechtfertigung – sondern auch beim Typ der Enumeration, also IEnumerable<> oder IQueryable<>.

Ich zumindest baue bei der Entwicklung gerade komplexe LINQ-Anweisungen oft in einzelnen Schritten auf, wodurch sich der Typ ständig ändert. Aber auch bei fertigem Code ist eine Aufteilung in mehrere Schritte keine Seltenheit, z.B. um deutlich zu machen, dass der LINQ-Provider gewechselt wird. Wenn hier Änderungen anstehen kann sich leicht eine falsche Deklaration einschleichen, ohne dass das offensichtlich wird.

Ein (konstruiertes) Beispiel:

   1: public IEnumerable<AllocationTimeSpan> GetAllTimeSpans()

   2: {

   3:     DateTime currentDate = DateTime.Today;

   4:     IEnumerable<AllocationTimeSpan> allTimeSpans = _context

   5:         .AllocationSet

   6:         .OfType<Allocation>()

   7:         .Where(a => a.AllocatedFrom <= currentDate && a.AllocatedTo >= currentDate)

   8:         .Select(a => new AllocationTimeSpan

   9:         {

  10:             From = a.AllocatedFrom,

  11:             To = a.AllocatedTo,

  12:             Status = (Status)(a.Status),

  13:         })

  14:         .Distinct()

  15:         .OrderBy(ts => ts.From)

  16:         .ToArray();

  17:     return allTimeSpans;

  18: }

Ein Aufruf gegen die Datenbank, samt Filterung, Sortierung, etc.. Keine große Auffälligkeit. Nur findet mir etwas zu viel auf einmal statt, also trenne ich das:

   1: public IEnumerable<AllocationTimeSpan> GetAllTimeSpans4b()

   2: {

   3:     // erstmal die daten...

   4:     IEnumerable<Allocation> allocations = _context

   5:         .AllocationSet

   6:         .OfType<Allocation>();

   7:     // dann selektion und projektion...

   8:     DateTime currentDate = DateTime.Today;

   9:     IEnumerable<AllocationTimeSpan> allTimeSpans = allocations

  10:         .Where(a => a.AllocatedFrom <= currentDate && a.AllocatedTo >= currentDate)

  11:         .Select(a => new AllocationTimeSpan

  12:         {

  13:             From = a.AllocatedFrom,

  14:             To = a.AllocatedTo,

  15:             Status = (Status)(a.Status),

  16:         })

  17:         .Distinct()

  18:         .OrderBy(ts => ts.From)

  19:         .ToArray();

  20:     return allTimeSpans;

  21: }

Eine Zwischenvariable eingeführt, nicht weiter nachgedacht, und schon geht die Abfrage ungefiltert gegen die Datenbank! Ein dummer Flüchtigkeitsfehler mit womöglich weitreichenden Auswirkungen. Vom Schreibaufwand mal ganz abgesehen.

 

Ergo: Der Einsatz von var reduziert sowohl den initialen Schreibaufwand, als auch den Änderungsaufwand. Und er eliminiert Fehlerquellen.

Tags: .NET Dev LINQ

2 Kommentare

06.05.20157:37 Uhr
Martin

Also entweder bin ich blöd, oder ich sehe hier im zweiten Beispiel kein "var"… zudem sehe ich nicht, warum die Abfrage ungefiltert auf die Datenbank gehen sollte (?). Schließlich findet bei der Zuweisung an "allocations" kein "ToList()" o.ä. statt.

Martin

08.05.20157:59 Uhr
Alexander Jung

Dass da kein "var" ist, sondern die lokale Variable mit IEnumerable typisiert wird, ist ja genau der Fehler. Extension methods werden nicht wie virtuelle Funktionen zur Laufzeit anhand des Typs des Objektes aufgelöst, sondern während der Compilierung anhand des Typs der Variable.

_context.AllocationSet.OfType() liefert als Ergebnis ein IQueryable(). Ruft man darauf – wie im ersten Beispiel – unmittelbar weitere Funktionen auf, so werden Extension-Methoden zu IQueryable<> gesucht und in Queryable gefunden, die Abfrage geht damit gegen die Datenbank.

Durch das Hinzufügen der temporären Variable „allocations“ geht diese Typinformation aber für den Compiler verloren. Extension-Methoden werden dann zu IEnumerable<> gesucht und in Enumerable gefunden. Und die laufen nun mal im Speicher ab.

Dein Kommentar wartet auf Freischaltung.

Artikel kommentieren

Zurück

Tag Cloud


Kontakt aufnehmen


Anrufen

Gerne beantworten wir Ihre Fragen in einem persönlichen Gespräch!


Kontakt aufnehmen

Schreiben Sie uns eine E-Mail mit Ihren Fragen und Kommentaren!