it-swarm.it

L'uso di LINQ e Lambda Expressions porta a un codice meno leggibile?

Sto discutendo con un collega su Linq, copierò qui:

Collaboratore: cerchiamo di essere onesti qui. La sintassi di Linq fa schifo. È confuso e non intuitivo.

Io: oh andiamo, più confuso di T-SQL?

Collaboratore: eh, si.

Io: ha le stesse parti base, seleziona, dove e da

Collaboratore: Linq, per me, è un bastardo di relazionale + OO. Collaboratore: non fraintendetemi: è incredibilmente potente, ma hanno riproposto SQL per utilizzare ancora raccolte di oggetti.

Sono dell'opinione che l'uso di Linq + Lamda sia molto potente (è d'accordo), e facilita anche la lettura del codice (non è d'accordo su questo punto):

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

o

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

o (codice VB qui)

   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

Per me questo è pulito e facile (almeno più facile delle alternative) da leggere, quali sono le tue opinioni al riguardo?

43
BlackICE

Non riesco più a trovare il post giusto, ma Eric Lippert (e forse molti altri software) hanno espresso più volte delle opinioni su come Linq sia dichiarativo, che, per diverse classi di problemi, è molto più intuitivo di imperativo sintassi.

Linq ti consente di scrivere codice che esprime l'intento , non il meccanismo .

Dimmi quale è più facile da leggere. Questo:

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);
    }
}

O questo?

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

O anche questo?

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

Il primo esempio è solo un mucchio di inutili scaldabagni per ottenere i risultati più semplici. Chiunque pensi che sia più leggibile rispetto alle versioni di Linq deve essere esaminato. Non solo, ma il primo spreca memoria. Non puoi nemmeno scriverlo usando yield return a causa dell'ordinamento.

Il tuo collega può dire quello che vuole; personalmente, penso che Linq abbia migliorato la mia leggibilità del codice incommensurabilmente.

Non c'è niente di "relazionale" in Linq. Può avere alcune somiglianze superficiali con SQL ma non tenta in alcun modo di implementare il calcolo relazionale. Sono solo alcune estensioni che semplificano l'interrogazione e la proiezione di sequenze. "Query" non significa "relazionale", e in effetti ci sono diversi database non relazionali che usano una sintassi simile a SQL. Linq è puramente orientato agli oggetti, funziona solo con database relazionali attraverso framework come Linq to SQL a causa di un voodoo di albero delle espressioni e di un design intelligente da il team C #, rendendo le funzioni anonime implicitamente convertibili in alberi delle espressioni.

74
Aaronaught

Collaboratore: cerchiamo di essere onesti qui. La sintassi di Linq fa schifo. È confuso e non intuitivo.

Non puoi discutere con questa critica. Per il tuo collega fa schifo . Non siamo riusciti a progettare una sintassi che, per loro, fosse chiara e intuitiva. Questo è il nostro fallimento e puoi trasmettere le mie scuse al tuo collega. Sono felice di dare suggerimenti su come migliorarlo; cosa trova in particolare il tuo collega confuso o non intuitivo?

Tuttavia, non puoi piacere a tutti. La mia opinione personale, e l'opinione della maggior parte delle persone con cui ho parlato sull'argomento, è che la sintassi della comprensione della query è molto più chiara della sintassi imperativa equivalente. Chiaramente non tutti sono d'accordo, ma fortunatamente non richiediamo il consenso di tutti i milioni di nostri clienti quando progettiamo il linguaggio.

Per quanto riguarda ciò che è "intuitivo", mi viene in mente la storia del linguista inglese che ha studiato molte lingue diverse e alla fine ho concluso che l'inglese era la migliore di tutte le lingue perché in inglese, le parole arrivano nello stesso ordine in cui le pensi. A differenza del francese, dove dicono costantemente cose come "il cane bianco mangia la carne rossa". Quanto deve essere difficile per i francesi pensare le parole in corrette e quindi devono dire nell'ordine francese ! Il francese è così poco intuitivo! È sorprendente che i francesi riescano a parlarlo. E tedesco? dove pensano "il cane mangia la carne" ma poi devono dire "il cane mangia la carne"!?! Così poco intuitivo.

Spesso ciò che è "intuitivo" è semplicemente una questione di familiarità. Mi ci sono voluti mesi di lavoro con LINQ prima che smettessi di iniziare le mie query con la clausola "select". Ora è una seconda natura e l'ordine SQL sembra bizzarro.

Che è! Le regole di scoping sono tutte incasinate in SQL. Qualcosa che potresti voler segnalare al tuo collega è che LINQ è stato accuratamente progettato in modo che (1) l'introduzione di variabili e ambiti avvenga da sinistra a destra (*) e (2) l'ordine in cui la query appare sulla pagina è l'ordine in cui viene eseguito. Cioè, quando dici

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

la c appare nel mirino a sinistra e rimane nel mirino a destra. E l'ordine in cui le cose accadono sono: vengono valutati i primi "clienti". Quindi il "dove" viene valutato per filtrare la sequenza. Quindi la sequenza filtrata viene proiettata dalla "selezione".

SQL non ha questa proprietà. Se dici

SELECT Name FROM Customers WHERE City = 'London'

quindi "Nome" viene portato nell'ambito da qualcosa alla sua destra, non alla sua sinistra, e la query viene eseguita in un ordine completamente incasinato; viene valutata prima la clausola centrale, quindi l'ultima clausola e quindi la prima clausola. Questo ora mi sembra folle e poco intuitivo, avendo lavorato esclusivamente con LINQ per così tanto tempo.


(*) Le regole di scoping sono un po 'strane in LINQ con clausole join. Ma a parte questo, gli ambiti nidificano bene.

69
Eric Lippert

Come qualsiasi altra cosa nel mondo della programmazione, devi abituarti alla sintassi e quindi è (potenzialmente) più facile da leggere.

Come qualsiasi altra cosa nel mondo della programmazione, esiste il potenziale per il codice spaghetti o altri abusi.

Come qualsiasi altra cosa nel mondo della programmazione, puoi farlo in questo modo o in un altro modo.

Come qualsiasi altra cosa nel mondo della programmazione, il tuo chilometraggio può variare.

22
Wonko the Sane

Ho visto un commento/osservazione in cui diceva qualcosa - in relazione a LINQ/lambda - sulla falsariga di: "Scrivi codice leggibile dagli umani, piuttosto che leggibile sul tuo computer".

Penso che questa affermazione abbia molto merito, tuttavia, considera lo sviluppatore (come me stesso) che ha attraversato l'intera gamma di linguaggi di sviluppo dall'Assemblea, attraverso procedurali, attraverso OO, attraverso gestiti, sfruttando soluzioni parallele con task ad alto rendimento .

Mi sono orgoglioso di rendere il mio codice il più leggibile e riutilizzabile possibile e di adottare molti dei principi del modello di progettazione GOF al fine di fornire sistemi e servizi di qualità di produzione in un ampio numero di settori aziendali diversi.

La prima volta che ho incontrato l'espressione lambda ho pensato: "Che diavolo è quello!?!" È stata immediatamente contro-intuitiva la mia sintassi esplicita esplicita familiare (e quindi comoda). I ragazzi più giovani <5yrs nel lavoro tuttavia hanno vomitato come se fosse una manna dal cielo!

Questo perché per anni pensare come un computer (in senso sintattico) tradotto molto facilmente in sintassi di codifica diretta (indipendentemente dalla lingua). Quando hai avuto quella mentalità computazionale per circa 20 + anni (nel mio caso 30+) devi capire che l'espressione sintattica iniziale shock dell'espressione lambda può facilmente tradursi in paura e diffidenza.

Forse il collaboratore del PO era venuto da un ambiente simile a me stesso (ovvero è stato in giro un paio di volte) ed era controintuitivo per loro in quel momento? La mia domanda è: cosa hai fatto al riguardo? Hai provato a rieducare il tuo pari a comprendere i benefici della sintassi inline o li hai saccheggiati/ostracizzati per non "essere con il programma"? Il primo avrebbe probabilmente visto il tuo collega avvicinarsi alla tua linea di pensiero, il secondo probabilmente li avrebbe diffidare ancora di più della sintassi LINQ/lambda e in tal modo esacerbando l'opinione negativa.

Per quanto mi riguarda, ho dovuto rieducare il mio modo di pensare (dato che Eric parla sopra, non è un cambiamento di mente insignificante, e ho dovuto programmare a Miranda negli anni '80, quindi ho avuto la mia parte di esperienza di programmazione funzionale) ma una volta superato quel dolore i benefici erano evidenti ma - cosa ancora più importante - dove il suo uso era sovrautilizzato (cioè usato per il gusto di usarlo), rispetto a complessi e ripetitivi (considerando il DRY in quell'istanza).

Dato che qualcuno che non solo scrive ancora molto codice ma che deve anche rivedere tecnicamente molto codice, era indispensabile comprendere questi principi in modo da poter rivedere gli elementi in modo imparziale, consigliare dove l'uso di un'espressione lambda potrebbe essere più efficiente/leggibile e anche per indurre gli sviluppatori a considerare la leggibilità di espressioni lambda inline altamente complesse (in cui una chiamata al metodo renderebbe, in quei casi, il codice più leggibile, gestibile ed estensibile).

Quindi quando qualcuno dice che "Non prendi lambda?" o sintassi LINQ, piuttosto che contrassegnarli come luddite cercare di aiutarli a comprendere i principi sottostanti. Dopotutto, possono avere un background di "vecchia scuola" come me.

6
CWC

Lambda Expressions porta a un codice meno leggibile se le query sono troppo lunghe. Tuttavia è molto meglio di troppi loop nidificati.

È meglio con un miscela dei due.

Scrivilo in Lambda se è più veloce (devi essere veloce) o più facile da leggere.

4
Amir Rezaei

Penso che dipenda nella maggior parte dei casi (tranne quando si fa qualcosa di molto bizzarro) se intendi "leggibile" come qualcuno che ha l'idea di cosa sta succedendo o se può facilmente trovare tutti i piccoli dettagli.

Penso che il link aiuti con il primo, ma spesso (specialmente durante il debug) fa male al secondo.

IMHO quando guardo il codice non ho familiarità con il primo è enormemente più importante del secondo, quindi lo trovo molto più leggibile.

1
Bill

Trovo che la sintassi LINQ sia intuitiva e facile da leggere, soprattutto perché mettono il FROM all'inizio dove appartiene invece che nel mezzo come in SQL. Ma i lambda IMO sono confusi e rendono il codice più difficile da leggere.

1
Mason Wheeler

Concordo con te sul fatto che la sintassi di Linq non è significativamente diversa da T-SQL. Penso che il tuo collega potrebbe davvero obiettare a cose relazionali che si mescolano in cui il suo Nice and shiny OO. D'altra parte, la programmazione funzionale richiede un po 'di tempo per abituarsi e la volontà di abituarsi.

1
Larry Coleman

Dipende. Ovviamente, T-SQL fornisce in modo univoco alcune soluzioni relazionali di DB. Ovviamente LINQ fornisce in modo univoco alcune soluzioni OO.

Però; "più confuso di T-SQL?" - è discusso/posto nella domanda iniziale. Ciò implica chiaramente alcune caratteristiche che nessuna delle risposte esistenti affronta, invece accusando il critico (ovviamente con conoscenza di SQL) di essere bloccato in passato.

Quindi, anche se apprezzo LINQ per alcune qualità e non sono molto in disaccordo con le risposte esistenti qui, sento che il contrappunto merita rappresentazione:

Anni dopo aver acquisito familiarità con LINQ, eseguire determinati tipi di operazioni di gruppo, join esterni e non-equijoin, lavorare con chiavi composite e altre operazioni in LINQ mi fanno ancora rabbrividire. (Soprattutto quando si prende di mira un back-end relazionale con domande sensibili alle prestazioni.)

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

Se pensi che sia intuitivo, più potere per te. ;)

0
shannon