2013-08-15 17 views
13

Spesso vedo (in molte librerie di mocking ad esempio) metodi in cui un argomento di tipo generico viene utilizzato al posto di un argomento di tipo System.Type. Sto specificatamente parlando di casi in cui il tipo generico viene utilizzato solo nell'operazione typeof(T) (ad esempio, nessuna istanza di tipo T viene utilizzata da nessuna parte all'interno del metodo e T non viene utilizzata per il tipo restituito o altri argomenti).Utilizzo di un argomento di tipo generico al posto di un argomento di tipo System.Type. È un odore?

Ad esempio si consideri seguente metodo:

public string GetTypeName(System.Type type) { return type.FullName; } 

questo metodo è spesso accompagnata con una versione generica:

public string GetTypeName<T>() { return GetTypeName(typeof(T)); } 

Domande E 'una cattiva pratica o di una buona pratica?
È uno zucchero sintattico o ce ne sono altri?

Io vedo questo come un uso improprio di una caratteristica del linguaggio per abbreviare una chiamata a un metodo che accetta un argomento di tipo System.Type

Vuoi considerare questo un odore? Questo dovrebbe essere evitato? o è in realtà una buona pratica (per fornire un metodo generico come scorciatoia per evitare di digitare typeof()).

Ecco alcune questioni pratiche con l'utilizzo di questo modello mi viene in mente:

  1. se si aggiunge un argomento di tipo non System.Type - metodo potrebbe bisogno di essere riscritto (se l'ordine degli argomenti è semanticamente significativo) alla versione non generica (altrimenti alcuni argomenti saranno argomenti di tipo generico, e alcuni saranno argomenti regolari).
  2. richiede due metodi (generici e non generici per i casi in cui il tipo non è noto in fase di compilazione). Di conseguenza aggiunge test unitari che sono per lo più privi di significato.

D'altra parte questa è una pratica comune (e la maggioranza ha sempre ragione, giusto?) Ma ancora più importante ReSharper preferisce che la firma quando faccio Estrarre Metodo refactoring su un codice che richiede solo argomento di tipo System.Type conosciuto al momento della compilazione (e ho imparato a prendere le loro raccomandazioni anche se non sulla fede, ma seriamente).

+7

Un zen koan in fondo - Un argomento di tipo tipo un argomento di tipo? E questo mi infastidisce anche me. – SWeko

+0

alla domanda è impossibile rispondere in modo definitivo. ciò che odora di uno può non sentire l'odore dell'altro. il fatto che il linguaggio supporti entrambi i casi significa che entrambe le forme sono implementazioni tecnicamente valide. – CedricB

risposta

0

I metodi che si occupano di tipi di solito fanno proprio questo: gestire i tipi.

IMO, Class.Method<SomeType>(); è molto meglio di Class.Method(typeof(SomeType));

Ma questa è una questione di opinione immagino.

consideri LINQ di .OfType<T>(), ad esempio:

personlist.OfType<Employee>().Where(x => x.EmployeeStatus == "Active"); 

versus:

personlist.OfType(typeof(Employee)).Where(x => ((Employee)x).EmployeeStatus == "Active"); 

quale preferisce?

+4

La firma del metodo 'OfType' è:' public static IEnumerable OfType (questo oggetto di tipo IEnumerable) viene utilizzato nel tipo restituito. Non è possibile riscrivere questo metodo usando typeof (Employee) senza ridurre il risultato a 'IEnumerable '. –

+0

quindi il tuo punto è che questo è uno zucchero sintattico e rende il codice ** molto ** migliore? –

+0

@ THX-1138 Forse è stato un cattivo esempio, la mia intenzione era di dimostrare la differenza di sintassi in entrambi i casi –

0

Come già detto, mi sembra una questione di preferenze personali.Dalla mia esperienza, la maggior parte dei metodi che accettano un argomento di tipo Tipo possono essere sintatticamente "addolciti" utilizzando un metodo di estensione associato.

Quindi, nel caso dell'esempio, renderei il secondo metodo un'estensione. Utilizzando questo approccio, ottieni una soluzione per la tua seconda preoccupazione: non sono necessari test unitari non necessari. Il primo, tuttavia, rimane; ma poi, l'aggiunta di argomenti richiederà comunque il refactoring, quindi fornirà l'opportunità di cambiare i consumatori del metodo di estensione in modo che utilizzino la versione modificata di quella originale.

Naturalmente, è solo un'opinione personale.

0

Un metodo generico presenta un vantaggio rispetto a un metodo con un parametro di tipo Type, in quanto può riferire ad altri elementi generici utilizzando il tipo fornito. Ciò è particolarmente utile nel seguente scenario:

public string GetTypeName<T>() 
{ 
    return Cache<T>.TypeName; 
} 

private static class Cache<T> 
{ 
    public static readonly TypeName = GetTypeName(typeof(T)); 
} 

Questa cache è facile, non c'è bisogno di pasticciare con i dizionari ed è thread-safe automaticamente.

Detto questo, se l'implementazione non sta utilizzando questa possibilità, la differenza tra i due è solo estetica.

3

Penso che sia necessario prendere in considerazione la documentazione. Quanto è ovvio ciò che fanno i metodi? Se hai due metodi (uno con Type e uno con un argomento di tipo), gli utenti devono esaminare entrambi e scegliere. Le persone che non guardano il tuo codice potrebbero non rendersi conto che il secondo semplicemente chiama il primo.

Dove è sensato usare entrambi è quando l'argomento tipo viene effettivamente utilizzato quando può essere e c'è una sorta di fallback per la versione Type. Ad esempio:

Un'altra cosa da considerare: un argomento di tipo deve essere sempre scritto in modo esplicito. Se è probabile che ci sarà più di una operazione da eseguire con lo stesso tipo di oggetto, non è utile usare gli argomenti di tipo. Prendere in considerazione qualcosa di simile:

var t = typeof(string); 
var name = GetTypeName(t); 
var assemblyName = t.Assembly.FullName; 

Anche se so il tipo è string, non dovrei scrivere GetTypeName<string> qui perché mi sarei ripeterò. Offrendomi un'opzione che preferirei spesso non scegliere, aggiungerai un po 'di complessità non necessaria.

Un punto più oscuro è il supporto IDE della documentazione XML. Documentare l'argomento di tipo simile a questo:

<typeparam name="T">important information</typeparam> 

Poi, se si digita GetTypeName< in C#, Visual Studio visualizza "T: informazioni importanti". Ma, per qualche motivo, quando si digita GetTypeName(Of in Visual Basic, non lo sarà (a partire dal 2012).

2

Hai ragione: considera la semantica del metodo. Funziona su istanze del tipo o sul tipo stesso?

Se funziona su istanze, dovrebbe essere un metodo generico. Se è presente nel tipo, rendi un argomento di tipo Type.

Quindi nel tuo esempio direi

public string GetTypeName(System.Type type) { return type.FullName; } 

Mentre

public static int Count<TSource>(this IEnumerable<TSource> source) 

funziona nell'istanza source di tipo IEnumerable<TSource>.

In generale, ho visto i generici maltrattati più che ben utilizzati. Qualsiasi implementazione di metodo generico che faccia un typeof (T) o peggio ancora, usi qualsiasi tipo di riflessione non è veramente generica secondo me ed è un abuso. Dopo tutto, generico significa che funziona allo stesso modo indipendentemente dall'argomento tipo, vero?

Quindi, in sintesi, sono d'accordo con te - odora.

1

Non uso il pattern string GetName<T>() { return typeof(T).Name; } in quanto è un uso improprio (l'uso improprio è probabilmente forte ma non riesco a pensare alla parola giusta) del modello di progettazione che è la ragione per i generici, vale a dire: i parametri di tipo generico sono lì per il compilatore e il JITter (vedi la risposta a this question) in modo che possano generare lo storage specifico del tipo, i parametri, le variabili dello stack, ecc.

Usarlo come un metodo conveniente per passare un argomento di tipo in fase di esecuzione a io odori. Ci sono momenti in cui è necessario il numero typeof(T), ma li ho trovati rari e di solito necessari solo quando si fanno cose complesse con i generici, al contrario di tipi di sicurezza di tipo semplice. Se lo vedo, mi fermo in silenzio e mi chiedo perché è lì.

Problemi correlati