it-swarm-eu.dev

Führt die Verwendung von LINQ- und Lambda-Ausdrücken zu weniger lesbarem Code?

Ich habe eine Diskussion mit einem Kollegen über Linq. Ich werde sie hier kopieren:

Mitarbeiter: Seien wir hier ehrlich. Die Linq-Syntax ist scheiße. Es ist verwirrend und nicht intuitiv.

Ich: Ach komm schon, verwirrender als T-SQL?

Mitarbeiter: Äh, ja.

Ich: Es hat die gleichen grundlegenden Teile, wählen Sie, wo und von

Mitarbeiter: Linq ist für mich eine Bastardisierung von relationalem + OO. Mitarbeiter: Verstehen Sie mich nicht falsch - es ist unglaublich leistungsfähig, aber sie haben SQL für die Verwendung von Objektsammlungen verwendet.

Ich bin der Meinung, dass die Verwendung von Linq + Lamda sehr leistungsfähig ist (er stimmt zu) und auch das Lesen von Code erleichtert (in diesem Punkt ist er anderer Meinung):

pickFiles = from f in pickFolder.GetFiles("*.txt")
where ValidAuditFileName.IsMatch(f.Name)
select f;

oder

var existing = from s in ActiveRecordLinq.AsQueryable<ScannedEntity>()
where s.FileName == f.FullName && s.DocumentType != "Unknown"
select s;

oder (VB-Code hier)

   Dim notVerified = From image In images.AsParallel
     Group Join verifyFile In verifyFolder.GetFiles("*.vfy").AsParallel.Where(
      Function(v) v.Length > 0
      ).AsParallel
   On image.Name.Replace(image.Extension, ".vfy") Equals verifyFile.Name
     Into verifyList = Group
    From verify In verifyList.DefaultIfEmpty
    Where verify Is Nothing
    Select verify

Für mich ist das sauber und leicht zu lesen (zumindest einfacher als die Alternativen). Wie ist Ihre Meinung dazu?

43
BlackICE

Ich kann den richtigen Beitrag nicht mehr finden, aber Eric Lippert (und möglicherweise mehrere andere Softies) haben mehrfach darüber nachgedacht, wie Linq deklarativ ist, was für verschiedene Problemklassen weitaus intuitiver ist als Imperativ Syntax.

Mit Linq können Sie Code schreiben, der die Absicht ausdrückt, nicht den Mechanismus.

Sie sagen mir, was leichter zu lesen ist. Diese:

IEnumerable<Customer> GetVipCustomers(IEnumerable<Customer> source)
{
    List<Customer> results = new List<Customer>();
    foreach (Customer c in source)
    {
        if (c.FirstName == "Aaron")
        {
            results.Add(c);
        }
    }
    results.Sort(new LastNameComparer());
    return results;
}

class LastNameComparer : IComparer<Customer>
{
    public int Compare(Customer a, Customer b)
    {
        return x.LastName.CompareTo(b.LastName);
    }
}

Oder dieses?

IEnumerable<Customer> GetVipCustomers(IEnumerable<Customer> source)
{
    return from c in source
           where c.FirstName == "Aaron"
           orderby c.LastName
           select c;
}

Oder auch das?

IEnumerable<Customer> GetVipCustomers(IEnumerable<Customer> source)
{
    return source.Where(c => c.FirstName == "Aaron").OrderBy(c => c.LastName);
}

Das erste Beispiel ist nur ein Bündel sinnloser Kesselplatten, um die einfachsten Ergebnisse zu erzielen. Jeder, der denkt, dass es mehr lesbar ist als die Linq-Versionen, muss seinen Kopf untersuchen lassen. Nicht nur das, sondern der erste verschwendet Speicher. Sie können es wegen der Sortierung nicht einmal mit yield return Schreiben.

Ihr Mitarbeiter kann sagen, was er will; Ich persönlich denke, Linq hat die Lesbarkeit von my Code unermesslich verbessert.

Auch an Linq ist nichts "relationales". Es kann einige oberflächliche Ähnlichkeiten mit SQL aufweisen, versucht jedoch in keiner Form, relationale Berechnungen zu implementieren. Es sind nur eine Reihe von Erweiterungen, die das Abfragen und Projizieren von Sequenzen erleichtern. "Abfrage" bedeutet nicht "relational", und es gibt tatsächlich mehrere nicht relationale Datenbanken, die eine SQL-ähnliche Syntax verwenden. Linq ist rein objektorientiert. Es funktioniert nur mit relationalen Datenbanken über Frameworks wie Linq to SQL, da das C # -Team einen Ausdrucksbaum-Voodoo und ein cleveres Design aufweist, das anonyme Funktionen implizit konvertierbar macht Bäume ausdrücken.

74
Aaronaught

Mitarbeiter: Seien wir hier ehrlich. Die Linq-Syntax ist scheiße. Es ist verwirrend und nicht intuitiv.

Mit dieser Kritik kann man nicht streiten. Für Ihren Kollegen ist es scheiße. Wir haben keine Syntax entworfen, die für sie klar und intuitiv war. Das ist unser Versagen, und Sie können meine Entschuldigung an Ihren Kollegen weitergeben. Gerne nehme ich Vorschläge zur Verbesserung entgegen. Was genau findet Ihr Mitarbeiter verwirrend oder nicht intuitiv?

Sie können jedoch nicht allen gefallen. Meine persönliche Meinung und die Meinung der meisten Leute, mit denen ich zu diesem Thema gesprochen habe, ist, dass die Syntax des Abfrageverständnisses viel klarer ist als die entsprechende imperative Syntax. Natürlich sind sich nicht alle einig, aber zum Glück brauchen wir nicht den Konsens aller Millionen unserer Kunden, wenn wir Sprachdesign betreiben.

In Bezug auf das, was "intuitiv" ist, erinnere ich mich an die Geschichte des englischen Sprachwissenschaftlers, der viele verschiedene Sprachen studierte und schließlich zu dem Schluss kam, dass Englisch die beste aller Sprachen ist, weil auf Englisch kommen die Wörter - in der gleichen Reihenfolge, in der Sie sie denken. Im Gegensatz zu Französisch, wo sie ständig Dinge sagen wie "der Hund weiß isst das Fleisch rot". Wie schwer muss es für die Franzosen sein, zu denken die Wörter in der Reihenfolge richtig und dann sagen sie in die Französisch Bestellung! Französisch ist so unintuitiv! Es ist erstaunlich, dass die Franzosen es schaffen, es zu sprechen. Und Deutsch? wo sie denken "der Hund frisst das Fleisch", müssen dann aber sagen "der Hund das Fleisch isst"!?! So unintuitiv.

Was "intuitiv" ist, ist oft nur eine Frage der Vertrautheit. Ich habe Monate mit LINQ gearbeitet, bevor ich aufgehört habe, meine Abfragen mit der "select" -Klausel zu beginnen. Jetzt ist es eine zweite Natur, und die SQL-Reihenfolge scheint bizarr.

Was es ist! Die Scoping-Regeln sind alle in SQL durcheinander. Sie möchten Ihren Kollegen möglicherweise darauf hinweisen, dass LINQ sorgfältig so konzipiert wurde, dass (1) die Einführung von Variablen und Bereichen von links nach rechts erfolgt (*) und (2) die Reihenfolge, in der die Abfrage auf der Seite angezeigt wird die Reihenfolge, in der es ausgeführt wird. Das heißt, wenn Sie sagen

from c in customers where c.City == "London" select c.Name

das c wird links im Bereich angezeigt und bleibt rechts im Bereich. Und die Reihenfolge, in der die Dinge passieren, ist: Zuerst werden "Kunden" bewertet. Dann wird das "Wo" ausgewertet, um die Sequenz zu filtern. Dann wird die gefilterte Sequenz durch die "Auswahl" projiziert.

SQL hat diese Eigenschaft nicht. Wenn du sagst

SELECT Name FROM Customers WHERE City = 'London'

dann wird "Name" durch etwas zu seiner Rechten und nicht zu seiner Linken in den Geltungsbereich gebracht, und die Abfrage wird in einer völlig durcheinandergebrachten Reihenfolge ausgeführt; Der mittlere Satz wird zuerst ausgewertet, dann der letzte Satz und dann der erste Satz. Das erscheint mir jetzt verrückt und unintuitiv, da ich schon so lange ausschließlich mit LINQ gearbeitet habe.


(*) Scoping-Regeln sind in LINQ mit Join-Klauseln etwas seltsam. Ansonsten nisten die Zielfernrohre gut.

69
Eric Lippert

Wie alles in der Programmierwelt muss man sich an die Syntax gewöhnen, und dann ist es (möglicherweise) einfacher zu lesen.

Wie bei allem anderen in der Programmierwelt besteht die Möglichkeit von Spaghetti-Code oder anderen Missbräuchen.

Wie alles in der Programmierwelt können Sie dies entweder auf diese oder eine andere Weise tun.

Wie bei allem anderen in der Programmierwelt kann Ihr Kilometerstand variieren.

22
Wonko the Sane

Ich habe einen Kommentar/eine Bemerkung gesehen, in der - in Bezug auf LINQ/Lambda - Folgendes angegeben ist: "Schreiben Sie Code, der für Menschen lesbar und nicht für Ihren Computer lesbar ist".

Ich denke, dass diese Aussage viel Verdienst hat. Betrachten Sie jedoch den Entwickler (wie mich selbst), der die gesamte Bandbreite der Entwicklungssprachen von Assembly über Prozedur, OO, Verwaltung bis hin zur Nutzung paralleler Lösungen für Aufgaben mit hohem Durchsatz durchlaufen hat .

Ich bin stolz darauf, meinen Code so lesbar und wiederverwendbar wie möglich zu machen und viele der GOF-Entwurfsmusterprinzipien zu übernehmen, um Produktionsqualitätssysteme und -dienstleistungen für eine Vielzahl unterschiedlicher Geschäftsbereiche bereitzustellen.

Als ich das erste Mal auf den Lambda-Ausdruck stieß, dachte ich: "Was zum Teufel ist das!?!" Es war sofort kontraintuitiv zu meiner vertrauten (und daher komfortablen) expliziten deklarativen Syntax. Die jüngeren <5 Jahre im Job Jungs haben es jedoch geläppt, als wäre es Manna vom Himmel!

Das liegt daran, dass jahrelang das Denken wie ein Computer (im syntaktischen Sinne) sehr leicht in eine direkte Codierungssyntax (unabhängig von der Sprache) übersetzt werden konnte. Wenn Sie diese rechnerische Denkweise für ca. 20+ Jahre (30+ in meinem Fall) hatten, müssen Sie verstehen, dass der anfängliche syntaktische Schock des Lambda-Ausdrucks kann leicht in Angst und Misstrauen übersetzt werden.

Vielleicht hatte der Mitarbeiter im OP einen ähnlichen Hintergrund wie ich (d. H. War ein paar Mal im Block) und es war zu dieser Zeit für sie nicht intuitiv? Meine Frage ist: Was hast du dagegen getan? Haben Sie versucht, Ihren Kollegen darin zu schulen, die Vorteile der Inline-Syntax zu verstehen, oder haben Sie sie angeprangert/geächtet, weil sie nicht "mit dem Programm" waren? Ersteres hätte wahrscheinlich gesehen, dass Ihr Mitarbeiter zu Ihrer Denkrichtung zurückgekehrt ist, letzteres würde ihn wahrscheinlich noch mehr dazu bringen, der LINQ/Lambda-Syntax zu misstrauen und damit die negative Meinung zu verschärfen.

Für mich selbst musste ich meine eigene Denkweise umerziehen (wie Eric oben schlussfolgert, ist es keine unbedeutende Veränderung, und ich musste in den 80ern in Miranda programmieren, damit ich meinen Anteil an funktionaler Programmiererfahrung hatte). Aber nachdem ich diesen Schmerz durchgemacht hatte, waren die Vorteile offensichtlich, aber - was noch wichtiger ist - wo seine Verwendung über verwendet wurde (dh verwendet wurde, um ihn zu verwenden ), überkomplex und repetitiv (unter Berücksichtigung des Prinzips DRY in diesem Fall).

Als jemand, der nicht nur noch viel Code schreibt, sondern auch viel Code technisch überprüfen muss, war es unerlässlich, dass ich diese Prinzipien verstanden habe, damit ich Elemente unparteiisch überprüfen und beraten kann, wo die Verwendung eines Lambda-Ausdrucks effizienter sein kann. lesbar und um Entwickler dazu zu bringen, die Lesbarkeit hochkomplexer Inline-Lambda-Ausdrücke zu berücksichtigen (wobei ein Methodenaufruf - in diesen Fällen - den Code lesbarer, wartbarer und erweiterbarer machen würde).

Also, wenn jemand sagt, dass er "kein Lambda bekommt?" oder LINQ-Syntax, anstatt sie als luddite zu brandmarken, versuchen Sie ihnen zu helfen, die zugrunde liegenden Prinzipien zu verstehen. Sie haben vielleicht doch einen "alten" Hintergrund wie ich.

6
CWC

Lambda-Ausdrücke führen zu weniger lesbarem Code, wenn die Abfragen zu lang sind. Es ist jedoch viel besser als zu viele verschachtelte Schleifen.

Es ist besser mit einem Mischung aus beiden.

Schreiben Sie es in Lambda, wenn es schneller ist (Sie müssen es schnell haben) oder einfacher zu lesen.

4
Amir Rezaei

Ich denke, es hängt in den meisten Fällen (außer wenn Sie etwas sehr Seltsames tun) davon ab, ob Sie "lesbar" meinen, wenn jemand die Idee hat, was los ist, oder ob er all die kleinen Details leicht finden kann.

Ich denke, Link hilft bei ersteren, aber oft (besonders beim Debuggen) schmerzt letztere.

IMHO, wenn ich Code betrachte, mit dem ich nicht vertraut bin, ist der erstere enorm wichtiger als der letztere, daher finde ich ihn viel lesbarer.

1
Bill

Ich finde die LINQ-Syntax intuitiv und einfach zu lesen, zumal sie das FROM an den Anfang setzt, wo es hingehört, anstatt wie in SQL in die Mitte. Aber IMO-Lambdas sind verwirrend und erschweren das Lesen des Codes.

1
Mason Wheeler

Ich stimme Ihnen zu, dass sich die Linq-Syntax nicht wesentlich von T-SQL unterscheidet. Ich denke, Ihr Kollege könnte wirklich dagegen sein, dass relationale Dinge gemischt werden, in denen sein netter und glänzender OO Code enthalten ist. Andererseits ist funktionale Programmierung etwas gewöhnungsbedürftig und gewillungsbereit.

1
Larry Coleman

Es hängt davon ab, ob. Offensichtlich bietet T-SQL in einzigartiger Weise einige DB-relationale Lösungen. Offensichtlich bietet LINQ in einzigartiger Weise einige OO -Lösungen).

Jedoch; "verwirrender als T-SQL?" - wird in der ersten Frage besprochen/gestellt. Dies impliziert eindeutig einige Merkmale, auf die sich keine der vorhandenen Antworten bezieht, und beschuldigt stattdessen den (offensichtlich mit SQL bekannten) Kritiker, in der Vergangenheit stecken geblieben zu sein.

Obwohl ich LINQ für bestimmte Eigenschaften schätze und den hier vorhandenen Antworten nicht sehr widerspreche, denke ich, dass der Kontrapunkt eine Darstellung verdient:

Jahre nachdem ich mich mit LINQ vertraut gemacht habe, erschrecke ich immer noch, wenn ich bestimmte Arten von Gruppenoperationen, äußeren Verknüpfungen und Nicht-Equijoins durchführe, mit zusammengesetzten Schlüsseln arbeite und andere Operationen in LINQ. (Insbesondere bei der Ausrichtung auf ein relationales Back-End mit leistungsabhängigen Fragen.)

from c in categories
join p in products on c equals p.Category into ps
from p in ps.DefaultIfEmpty()

Wenn Sie denken, dass dies intuitiv ist, haben Sie mehr Leistung. ;)

0
shannon