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


43

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?


11
Gli umani, in generale, odiano il cambiamento. Una grande percentuale lo odia così tanto che in realtà lo temono.
Tony,

9
Ammettiamolo ... linq è solo una stupida sintassi di programmazione funzionale aggiunta a C # e VB.Net. Bashare le espressioni linq e lambda in pratica sta dicendo "FP sucks". Questo è quello che sta dicendo il tuo collega. Penso che il dibattito sia stato portato altrove.
Scott Whitlock,

48
Dà fastidio a qualcun altro che le persone tendano a usare la parola "intuitivo" quando significano veramente "familiare"?
Larry Coleman,

7
Comunque, il team di C # (incluso Eric Lippert) ha fatto di tutto per spiegare che Linq non era un porting di SQL, ma è stato progettato da zero come la maggior parte delle altre funzionalità. Devo dire che il tuo collega è un luddista.
Aaronaught il

16
Per quello che vale: ho appena visto mia moglie (amministratore dell'ufficio - quasi zero esperienza di programmazione pratica) esaminare i 3 esempi di aaronaught e sono stato in grado di decifrare l'intento degli esempi linq e lambda molto più facilmente del tradizionale esempio imperativo.
Steven Evers,

Risposte:


73

Non riesco più a trovare il post giusto, ma Eric Lippert (e forse molti altri software) hanno discusso in diverse occasioni su come Linq sia dichiarativo , il che, per diverse classi di problemi, è molto più intuitivo della sintassi imperativa .

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 Linq ha bisogno di farsi esaminare la testa. Non solo, ma il primo spreca memoria. Non puoi nemmeno scriverlo usando a yield returncausa dell'ordinamento.

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

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 po 'di voodoo dell'albero delle espressioni e di un design intelligente da parte del team C #, rendendo le funzioni anonime implicitamente convertibili in alberi delle espressioni.


6
+1 Se non ti piace LINQ è probabilmente perché "non lo stai facendo bene" :)
Darknight il

1
Linq is purely object-oriented+1 per questo. Questo è anche il motivo per cui la nostra guida di stile del team impone l'utilizzo della sintassi fluida sulla sintassi della query. Trovo che renda la natura OO del collegamento più ovvia a qualcuno abituato a obiettare la notazione in linguaggi simili a C.
SBI,

1
So che il post ha 7 anni. Ma ecco la domanda: hai usato i linguaggi funzionali come strumenti principali prima di incontrare Linq?
Sergey.quixoticaxis.Ivanov,

4
Francamente, preferisco il foor loop, perché poi vedo subito dove possiamo e avremo eccezioni di riferimento NULL, come viene gestito null e non è l'esecuzione differita che rende difficile il debug e non crea n list anche, quindi il for-loop è anche più efficiente.
Quandary

1
@Aaronaught, Se si rimuove l'ordinamento e si riformatta il codice procedurale in stile Horstmann , dirò che è più leggibile rispetto alla versione LINQ . E secondo il principio della singola responsabilità, l'ordinamento in realtà non appartiene GetVipCustomers(), che, come suggerisce il nome stesso, restituirà solo una raccolta di clienti VIP, in ordine arbitrario. Nei rari casi in cui l'ordine è importante, come l'output sullo schermo, lasciare che il chiamante ordini la raccolta.
Ant_222,

69

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 specificamente il tuo collega confuso o poco 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 per fortuna non abbiamo bisogno del consenso di tutti i milioni di nostri clienti quando progettiamo la lingua.

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 pensali . A differenza del francese, dove costantemente dicono cose come "il cane bianco mangia la carne rossa". Quanto sia difficile deve essere per i francesi a pensare le parole del corretto ordine e poi a dire che loro in francese ordine! 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"!?!

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. A parte questo, gli ambiti nidificano bene.


5
Nitpick: In tedesco, "Der Hund frisst das Fleisch" è in realtà lo stesso ordine che in inglese, "Il cane mangia la carne" :) A parte questo, ho trovato la sintassi LINQ Query molto leggibile quando ho capito che non era SQL .
Michael Stum

18
@gbjbaanb: non sto giustificando la sintassi LINQ sulla base del tutto soggettiva della sua comprensione . Piuttosto, lo sto giustificando su una base del tutto obiettiva che è molto più facile progettare strumenti che aiutino l'utente nella costruzione di query LINQ perché la sintassi LINQ è stata progettata pensando agli strumenti e che è più facile capire mentalmente il ordine in cui si verificano gli eventi in una query perché arrivano nello stesso ordine in modo lessicale.
Eric Lippert,

2
Eric, Dal punto di vista della programmazione dello stile dichiarativo, capisco che Linq è appropriato (posso dire leggibile) di SQL. Ma è più leggibile dall'uomo di SQL? Non c'è modo. Se 'il cane mangia carne rossa' è difficile, quanto è più facile 'dalla cucina dove il colore è rosso prendi il coltello'? In effetti tutti parliamo e pensiamo come "prendi quel coltello che è in cima al tavolo dalla cucina". Ed è qui che SQL è più vicino alla vita reale di LINQ.
nawfal,

6
Voglio una tazza di caffè da Starbucks dove il negozio è aperto fino a mezzanotte. Ti suona familiare? È nello stesso ordine esatto di una query SQL. Sì, LINQ potrebbe essere più leggibile del codice non necessario che devi generare per eseguire una query SQL standard, ma LINQ non è più leggibile della query SQL stessa. Quindi sì; potrebbe essere vantaggioso per gli sviluppatori LINQ passare da sinistra a destra, ma come utente, perché me ne importa? Mi interessa solo l'usabilità.
KyleM

8
@KyleM: Certo; l'usabilità era un aspetto molto importante del design LINQ. In particolare, la chiave è stata la sensibilità agli strumenti di produttività . Poiché l'ambito scorre da sinistra per scrivere in una query LINQ, quando si digita c.l'IDE conosce già il tipo di ce può darti IntelliSense. In LINQ dici "da c nei clienti dove c." e boom, ottieni IntelliSense che ti aiuta elencando i membri di Customer. In SQL quando digiti "SELEZIONA il nome DAI clienti" non puoi ottenere alcun aiuto IDE per dirti che "nome" è una buona scelta perché hai digitato name prima customers .
Eric Lippert

22

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.


6

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!?!" Fu immediatamente contro-intuitivo per la mia sintassi esplicita familiare (e quindi comoda) esplicita. I più piccoli <5 anni nel lavoro ragazzi tuttavia lo hanno vomitato come se fosse la 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 (30+ nel mio caso) devi capire che lo shock sintattico iniziale 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 per il blocco alcune 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 che avevo sofferto di quel dolore i benefici erano evidenti ma - soprattutto - dove il suo uso era sovrautilizzato (cioè usato per il gusto di usarlo), piuttosto complesso e ripetitivo (considerando il principio DRY in quel caso).

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 la sintassi LINQ, piuttosto che di marca loro un luddista cercare di aiutarli a comprendere i principi di fondo. Dopotutto, possono avere un background di "vecchia scuola" come me.


Commento interessante: ho avuto questo quando sono passato da linguaggi statici fortemente tipizzati a linguaggi dinamici. Mi ci è voluto un po 'di tempo per adattarmi (paura e sfiducia) e ora trovo difficile tornare indietro, onestamente.
lunchmeat317,

4

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

È meglio con una miscela dei due .

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


Sì, ma cosa renderebbe linq / lamda non più facile da leggere?
BlackICE il

scusate, significava non più facile da leggere rispetto all'alternativa.
BlackICE il

1

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.


Buon punto. Quest'ultimo è generalmente coperto da un buon test unitario.
Scott Whitlock,

Quindi pensi che gli interni della valutazione Linq rendano difficile trovare tutti i dettagli? Potete fornire un esempio di quando potrebbe essere?
BlackICE,

3
@David: le espressioni Linq non sono solo valutate in modo pigro, ma sono espressioni . Il codice di ricezione di un'espressione linq può effettivamente modificare l'espressione in modo che faccia qualcosa di completamente diverso, come una macro lisp che lavora su s-espressioni. Ad esempio, quando si lavora con linq in SQL, in realtà prende l'espressione where e.FirstName == "John"e la traduce in una query SQL! Lo fa guardando l'espressione non compilata (ma analizzata), vedendo che è un confronto di una proprietà chiamata FirstNamesu un'entità persistente, e si sta confrontando con una stringa, ecc. C'è molto da fare.
Scott Whitlock,

@david generalmente non trovo i dettagli difficili da trovare fino a quando non inizi a usare linq contro se stesso. Un esempio "semplice" di questo che mi ha richiesto molto tempo per avvolgere la mia testa è questo: bugsquash.blogspot.com/2008/07/y-combinator-and-linq.html ma ci sono molti altri esempi necessariamente in alcuni delle cose che le persone fanno con le espressioni nelle aree dinamica, DynamicObject e IDynamicMetaObjectProvider. Il punto in cui si traccia la linea su ciò che è linq è discutibile, ma come sottolinea scott tutta quella roba è disponibile per / in linq perché linq usa gli stessi alberi delle espressioni.
Bill

@Bill ok, ti ​​darò che è difficile, ma le funzioni di combinazione y e di ordine superiore danneggeranno la maggior parte delle nostre teste, è come una ricorsione, la capisci o no. L'implementazione ricorsiva di questo è più facile da leggere (se non si conosceva nessuno dei due)?
BlackICE,

1

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.


Perché pensi che i lambda siano confusi? È l'inferenza del tipo?
ChaosPandion,

1
@ChaosPandion: prima l'inferenza del tipo e, in secondo luogo, il simbolo. Mettere insieme un = e un> sembra un> = che qualcuno ha rovinato e scritto all'indietro, e tende a gettare il mio cervello per un ciclo.
Mason Wheeler,

@Mason - Ti piace la sintassi di Haskell ( \x -> x * x) o F # ( fun x -> x * x)?
ChaosPandion,

@ChaosPandion: No, non particolarmente. Le lambda possono essere veloci da scrivere, ma le trovo difficili da analizzare, specialmente quando diventano non banali nella complessità. I tuoi esempi non sono poi così male, ma tendono a peggiorare molto.
Mason Wheeler,

6
@Mason: Sembra che tu stia obiettando che i lamdas vengono abusati, piuttosto che obiettare all'idea dei lamdas in primo luogo.
Anon.

1

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 simpatico e brillante codice OO. D'altra parte, la programmazione funzionale richiede un po 'di tempo per abituarsi e la volontà di abituarsi.


0

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

Tuttavia; "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. ;)


-4

Probabilmente c'è un motivo per cui Linq fa schifo, prova solo a fare esempi del mondo reale, invece di questi esempi di libri di testo.

prova a mostrare questa funzione in linqlamdathings e vedrai che tutta la bellezza è sparita, mentre il modo classico rimane leggibile. Per non parlare dei problemi di esecuzione differita e impostazione dei punti di interruzione.

La verità è che LinqLambda Le cose sono molto utili in pochi casi e non si pensa che sostituiscano tutto l'ottimo orientamento dell'oggetto che voi ragazzi non avete mai capito

    public IList<Customer> GetVipCustomers(IList<Customer> source, int maxCount)
    {
        var results = new SortedList<string,Customer>();

        foreach (Customer c in source)
        {
            if (maxCount == results.Count)
                break;

            if (c.IsVip && c.FirstName== "Aaron" || c.SecondName== "Aaron")
                results.Add(c.LastName, c);
        }

        return results.Values;
    }

6
Penso che sia source.Where(c => c.IsVip && c.FirstName == "Aaron" || c.SecondName == "Aaron").Take(maxCount).OrderBy(d => d.LastName);difficile da leggere, quindi potrei aver fatto un errore;)
jk.

1
In che modo li hai considerati esempi di libri di testo? Questo è in realtà il codice d'uso della produzione. Sarebbe utile se fornissi sia il tuo classico codice "leggibile" sia la versione linq / lamda per il confronto invece di aspettarti che tutti lo convertano.
BlackICE

3
Ti sei registrato specificamente per lamentarti di LINQ?
KChaloux,

1
@KChaloux Penso che stessero trollando per essere onesti
jk.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.