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

41

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

Co-Worker: Lets be honest here. Linq syntax sucks. It's confusing and non-intuitive.

Me: oh come on, more confusing than T-SQL?

Co-Worker: uh, yes.

Me: it has the same basic parts, select, where, and from

Co-Worker: Linq, to me, is a bastardization of relational + OO. Co-Worker: Don't get me wrong - it's incredibly powerful, but they repurposed SQL to use agains object collections.

Sono dell'opinione che usare Linq + Lamda sia molto potente (è d'accordo), e rende anche il codice più facile da leggere (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 è pulito e facile (almeno più facile delle alternative) da leggere, quali sono le tue opinioni in merito?

    
posta BlackICE 09.12.2010 - 16:14
fonte

10 risposte

73

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

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

Dimmi che è 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 hotplan inutili al fine di ottenere il più semplice dei risultati. Chiunque pensi che sia più leggibile rispetto alle versioni 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" anche per Linq. Potrebbe avere alcune somiglianze superficiali con SQL ma non tenta in nessuna forma o forma di implementare il calcolo relazionale. Sono solo alcune estensioni che semplificano la ricerca e la proiezione di sequenze. "Query" non significa "relazionale", e ci sono in effetti diversi database non relazionali che usano la sintassi di tipo SQL. Linq è puramente orientato agli oggetti, funziona solo con database relazionali attraverso framework come Linq a SQL a causa di alcune espressioni tree voodoo e design intelligente del team C #, rendendo le funzioni anonime implicitamente convertibili in espressione alberi.

    
risposta data 10.12.2010 - 00:52
fonte
69

Co-Worker: Lets be honest here. Linq syntax sucks. It's confusing and non-intuitive.

Non puoi litigare con quella 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 passare le mie scuse al tuo collega. Sono felice di dare suggerimenti su come migliorarlo; che cosa rileva in modo particolare il tuo collega confuso o non intuitivo?

Tuttavia, non puoi accontentare tutti. La mia opinione personale, e l'opinione della maggior parte delle persone con cui ho parlato sull'argomento, è che la sintassi della comprensione delle 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 la lingua.

A proposito di ciò che è "intuitivo", però, mi viene in mente la storia del linguista inglese che ha studiato molte lingue diverse e alla fine ha concluso che l'inglese era la migliore di tutte le lingue perché in inglese, le parole arrivano nello stesso ordine in cui li pensi . A differenza del francese, dove dicono continuamente cose come "il cane bianco mangia la carne rossa". Quanto deve essere difficile per i francesi pensare le parole nell'ordine correggere e poi dover dirle nel francese ordine! Il francese è così poco intuitivo! È incredibile che i francesi riescano a parlarne. E tedesco? dove pensano "il cane mangia la carne" ma poi devono dire "il cane mangia la carne"!?! Quindi non intuitivo.

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

Quale è! Le regole di scoping sono tutte incasinate in SQL. Qualcosa che potresti voler segnalare al tuo collega è che LINQ è stato attentamente 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 sia l'ordine in cui viene eseguito. Cioè, quando dici

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

la c appare nello scope a sinistra e rimane nel campo di applicazione a destra. E l'ordine in cui le cose accadono sono: prima vengono valutati i "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 messo in campo da qualcosa alla sua destra, non alla sua sinistra, e la query viene eseguita in un ordine completamente incasinato; la clausola centrale viene valutata per prima, quindi l'ultima clausola e quindi la prima clausola. Ora mi sembra pazzesco e non intuitivo, avendo lavorato esclusivamente con LINQ per così tanto tempo.

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

    
risposta data 13.12.2010 - 08:19
fonte
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, c'è il potenziale per 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.

    
risposta data 09.12.2010 - 16:42
fonte
5

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

Penso che questa affermazione abbia molto valore, tuttavia, considera lo sviluppatore (come me) che ha utilizzato tutta la gamma di linguaggi di sviluppo da Assembly, procedurale, tramite OO, attraverso la gestione, sfruttando l'elevato throughput soluzioni parallele per attività.

Mi sono vantato di rendere il mio codice leggibile e riutilizzabile il più 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 eterogenei.

La prima volta che ho incontrato l'espressione lambda ho pensato: "Che diavolo è quello!?!" È stato immediatamente contro-intuitivo alla mia sintassi dichiarativa esplicita familiare (e quindi constrongvole). Il più giovane < Tuttavia, 5 anni nel lavoro i ragazzi l'hanno leccato come se fosse una manna dal cielo!

Questo perché per anni il pensiero come un computer (nel senso sintattico) si è tradotto molto facilmente nella sintassi di codifica diretta (indipendentemente dal linguaggio). Quando hai avuto quella mentalità computazionale per circa 20 + anni (30+ nel mio caso) devi apprezzare che l'iniziale shock sintattico dell'espressione lambda può facilmente tradursi in paura e sfiducia.

Forse il collaboratore dell'OP era venuto da un background simile a me (cioè sono stato intorno al blocco un paio di volte) ed era contro-intuitivo per loro in quel momento? La mia domanda è: cosa hai fatto a riguardo? Hai provato a rieducare il tuo pari a comprendere i vantaggi della sintassi in linea, o hai procurato / ostracizzato per non essere "con il programma"? Il primo probabilmente avrebbe visto il tuo collega collaborare alla tua linea di pensiero, il secondo probabilmente li avrebbe sfiduciati sulla sintassi LINQ / lambda ancora di più e quindi esacerberebbe l'opinione negativa.

Per me stesso ho dovuto rieducare il mio modo di pensare (come cita Eric sopra, non è un cambiamento di mente insignificante, e ho dovuto programmare in Miranda negli anni '80 così ho avuto la mia parte di funzionale esperienza di programmazione) ma una volta passato quel dolore i benefici erano ovvi ma - ancora più importante - dove il suo utilizzo era over used (cioè usato per il gusto di usarlo), troppo complesso e ripetitivo (considerando il principio DRY in quell'istanza).

Come qualcuno che non solo scrive ancora molto codice, ma anche che deve revisionare tecnicamente molto codice, era imperativo che io comprendessi questi principi in modo da poter esaminare gli oggetti in modo imparziale, indicare dove può essere l'uso di un'espressione lambda più efficiente / leggibile, e anche per convincere gli sviluppatori a considerare la leggibilità di espressioni lambda inline estremamente complesse (dove una chiamata al metodo - in quei casi - renderebbe il codice più leggibile, mantenibile ed estensibile).

Quindi, quando qualcuno dice che "Non prendi lambda?" o sintassi LINQ, piuttosto che etichettarli come luddite cercare di aiutarli a comprendere i principi sottostanti. Dopo tutto, potrebbero avere uno sfondo "vecchia scuola" come me.

    
risposta data 28.03.2013 - 20:04
fonte
4

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

È meglio con una combinazione dei due .

Scrivilo in Lambda se è più veloce (ne hai bisogno per essere veloce) o più facile da leggere.

    
risposta data 09.12.2010 - 16:56
fonte
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 riesce a trovare facilmente tutti i piccoli dettagli.

Penso che il link aiuti con il primo, ma spesso (specialmente quando si esegue il debug) fa male quest'ultimo.

IMHO quando guardo codice che non conosco il primo è estremamente più importante di quest'ultimo, quindi lo trovo molto più leggibile.

    
risposta data 09.12.2010 - 17:08
fonte
1

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

    
risposta data 09.12.2010 - 17:38
fonte
1

Sono d'accordo con te sul fatto che la sintassi di Linq non è significativamente diversa da T-SQL. Penso che il tuo collega potrebbe davvero essere contrario alle cose relazionali che si mescolano in cui il suo codice OO bello e lucido. D'altra parte, la programmazione funzionale richiede un po 'di abituarsi e la volontà di abituarsi ad essa.

    
risposta data 09.12.2010 - 22:56
fonte
0

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

Tuttavia; "più confuso di T-SQL?" - è discusso / chiesto nella domanda iniziale. Ciò implica chiaramente alcune caratteristiche che non rispondono a nessuna delle risposte esistenti, accusando invece il critico (ovviamente informato di SQL) di essere rimasto bloccato nel passato.

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

Anni dopo aver acquisito familiarità con LINQ, l'esecuzione di determinati tipi di operazioni di gruppo, join esterni e non equijoin, utilizzo di chiavi composite e altre operazioni in LINQ mi fanno ancora rabbrividire. (Soprattutto quando si mira a 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. ;)

    
risposta data 25.02.2015 - 08:50
fonte
-4

C'è probabilmente una ragione per cui Linq fa schifo, basta provare a fare esempi reali, invece di questi esempi da manuale.

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

La verità è che LinqLambdaThings è molto utile in alcuni casi e non sono pensati per sostituire tutto l'orientamento di un oggetto elegante 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;
    }
    
risposta data 14.03.2013 - 14:47
fonte

Leggi altre domande sui tag