Che cos'è l'abuso di farmaci generici?


35

Durante la revisione di alcuni codici, ho notato l'opportunità di cambiarlo per usare generici. Il codice (offuscato) è simile a:

public void DoAllTheThings(Type typeOfTarget, object[] possibleTargets)
{
    var someProperty = typeOfTarget.GetProperty(possibleTargets[0]);
    ...
}

Questo codice potrebbe essere sostituito da generici, in questo modo:

public void DoAllTheThings<T>(object[] possibleTargets[0])
{
    var someProperty = type(T).getProperty(possibleTargets[0]);
    ...
}

Nella ricerca dei vantaggi e delle carenze di questo approccio ho trovato un termine chiamato abuso generico . Vedere:

La mia domanda si presenta in due parti:

  1. Ci sono dei vantaggi nel passare a farmaci generici come questo? (Prestazioni? Leggibilità?)
  2. Che cos'è l'abuso di Generics? E sta usando un generico ogni volta che c'è un parametro di tipo un abuso ?

2
Non ho capito bene. Conosci il nome della proprietà in anticipo ma non il tipo di oggetto, quindi usi la riflessione per ottenere un valore?
MetaFight

3
@MetaFight Lo so esempio contorto, il codice reale sarebbe troppo lungo e difficile da mettere qui. Indipendentemente da ciò, lo scopo della mia domanda è determinare se la sostituzione del parametro Type con un generico sia considerata buona o cattiva forma.
Sintassi Regole

12
Ho sempre considerato questo il motivo tipico per cui esistono i generici (delizioso gioco di parole non inteso). Perché digitare l'inganno da soli quando puoi sfruttare il tuo sistema di tipi per farlo per te?
MetaFight

2
il tuo esempio è strano, ma ho la sensazione che sia esattamente quello che fanno molte librerie IoD
Ewan

14
Non è una risposta, ma vale la pena notare che il secondo esempio è meno flessibile del primo. Rendere la funzione generica rimuove la possibilità di passare in un tipo di runtime; I generici devono conoscere il tipo al momento della compilazione.
KChaloux,

Risposte:


87

Quando i generici vengono applicati in modo appropriato, rimuovono il codice anziché semplicemente riorganizzarlo. In primo luogo, il codice che i generici sono migliori nella rimozione sono i typecasts, la reflection e la tipizzazione dinamica. Pertanto, l'abuso di farmaci generici potrebbe essere definito come la creazione di codice generico senza una significativa riduzione della tipografia, della riflessione o della tipizzazione dinamica rispetto a un'implementazione non generica.

Per quanto riguarda il tuo esempio, mi aspetterei un uso appropriato dei generici per cambiare object[]in T[]o simile, ed evitare Typeo del typetutto. Ciò potrebbe richiedere un significativo refactoring altrove, ma se l'uso di generici è appropriato in questo caso, alla fine dovrebbe essere più semplice.


3
Questo è un ottimo punto. Devo ammettere che il codice che mi ha fatto pensare a questo stava facendo qualche riflessione difficile da leggere. Vedrò se posso passare object[]a T[].
Sintassi Regole

3
Mi piace la caratterizzazione remove vs. riorganizzare.
Copper.hat

13
Hai perso un uso chiave dei generici, che è quello di ridurre la duplicazione. (Anche se puoi ridurre la duplicazione introducendo uno di quegli altri peccati che menzioni). Se riduce la duplicazione senza rimuovere alcun tipo di digitazione, riflessione o digitazione dinamica IMO, va bene.
Michael Anderson,

4
Risposta eccellente. Aggiungo che in questo caso particolare, potrebbe essere necessario che l'OP passi a un'interfaccia anziché a generici. Sembra che il riflesso venga utilizzato per accedere a proprietà particolari dell'oggetto; un'interfaccia è molto più adatta per consentire al compilatore di determinare che tali proprietà esistano effettivamente.
jpmc26,

4
@MichaelAnderson "rimuovi il codice invece di risistemarlo" include la riduzione della duplicazione
Caleth

5

Vorrei usare la regola senza fronzoli: i generici, come tutti gli altri costrutti di programmazione, esistono per risolvere un problema . Se non ci sono problemi da risolvere per i generici, usarli è un abuso.

Nel caso specifico dei generici, esistono principalmente per astrarre da tipi concreti, consentendo alle implementazioni di codice per i diversi tipi di essere raggruppate in un modello generico (o come la si chiama per caso). Supponiamo ora di avere un codice che utilizza un tipo Foo. Potresti sostituire quel tipo con un generico T, ma se hai sempre bisogno di quel codice con cui lavorare Foo, semplicemente non c'è nessun altro codice con cui puoi piegarlo insieme. Pertanto, non esiste alcun problema da risolvere, quindi il riferimento indiretto dell'aggiunta del generico servirebbe solo a ridurre la leggibilità.

Di conseguenza, suggerirei di scrivere semplicemente il codice senza usare generici, fino a quando non vedrai la necessità di introdurli (perché hai bisogno di una seconda istanza). Quindi, e solo allora, è il momento di riformattare il codice per usare i generici. Qualsiasi uso prima di quel punto è un abuso nei miei occhi.


Sembra un po 'troppo pedante, quindi lascia che te lo ricordi:
non esiste una regola senza eccezioni nella programmazione. Questa regola inclusa.


Pensieri interessanti. Tuttavia, potrebbe essere utile una leggera riformulazione dei criteri. Supponiamo che OP abbia bisogno di un nuovo "componente" che associ un int a una stringa e viceversa. È ovvio che risolve un bisogno comune e potrebbe essere facilmente riutilizzabile in futuro se reso generico. Questo caso rientrerebbe nella tua definizione di abuso di farmaci generici. Tuttavia, una volta identificata la necessità di base molto generica, non varrebbe la pena investire uno sforzo aggiuntivo insignificante nella progettazione senza che questo sia un abuso?
Christophe

@Christophe Questa è davvero una domanda difficile. Sì, ci sono alcune situazioni in cui è davvero meglio ignorare la mia regola (non esiste una regola senza eccezioni nella programmazione!). Tuttavia, il caso specifico della conversione di stringhe è in realtà piuttosto coinvolto: 1. È necessario trattare i tipi interi con segno e senza segno separatamente. 2. È necessario trattare le conversioni float separate dalle conversioni intere. 3. È possibile riutilizzare un'implementazione per i tipi di numeri interi disponibili più grandi per i tipi più piccoli. Potresti voler scrivere attivamente un wrapper generico per fare il casting, ma il core sarebbe senza generici.
cmaster

1
Discuterei contro la scrittura proattiva di un generico (pre-uso anziché riutilizzo) come probabile YAGNI. Quando ne hai bisogno, quindi refactor.
Neil_UK

Al momento di scrivere questa domanda ho preso più posizione di "Scrivilo come un generico se possibile per salvare il possibile riutilizzo futuro". Sapevo che era un po 'estremo. È rinfrescante vedere il tuo punto di vista qui. Grazie!
Sintassi Regola

0

Il codice sembra strano. Ma se lo stiamo chiamando, sembra più bello specificare il tipo come generico.

https://stackoverflow.com/questions/10955579/passing-just-a-type-as-a-parameter-in-c-sharp

Personalmente non mi piacciono queste contorsioni che rendono carino il codice chiamante. per esempio l'intera cosa "fluente" e metodi di estensione.

Ma devi ammettere che ha un seguito popolare. perfino microsoft lo usa in unità per esempio

container.RegisterType<IInterface,Concrete>()

0

Per me sembra che tu fossi sulla strada giusta qui, ma non hai finito il lavoro.

Hai preso in considerazione la modifica del object[]parametro Func<T,object>[]per il passaggio successivo?

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.