2009-02-27 4 views

risposta

11

La principale differenza funzionale è che è possibile conoscere il tipo effettivo dell'oggetto all'interno del metodo generico. Il parametro T conterrà il tipo effettivo che può essere vantaggioso in determinati scenari.

Nel caso non generico non è possibile garantire l'accesso al tipo sottostante dell'oggetto. La maggior parte del tipo si può prendere valore.GetType() ma l'utente potrebbe passare Null e ostacolarvi.

+0

Non è corretto, sta limitando T a essere un IMyInterface. – joshperry

+0

@joshperry, sì ma T continuerà a puntare al tipo effettivo del valore passato. Più precisamente indicherà il tipo di riferimento nel callsite del metodo. Sono sicuro al 100% di essere corretto su questo punto. – JaredPar

+0

Sì, certo che ho interpretato erroneamente la tua affermazione. Chiedo scusa. – joshperry

3

La versione generica richiede .NET 2.0.

Ma seriamente, mentre sembrano simili, ci sono differenze fondamentali tra loro. Una differenza è che, in fase di esecuzione, il compilatore JIT genererà il codice per ciascun tipo di valore che verrà utilizzato per la versione generica. La versione non generica richiederà i tipi di valore da inscatolare per essere passati alla funzione.

La differenza è importante anche quando si ha a che fare con i delegati. La firma di MyMethod<int> corrisponde a void MyDelegate(int x) mentre la versione non generica non corrisponde.

7

Jared ha menzionato alcuni dei punti; un altro interessante: con i generici, è possibile evitare il pugilato dei tipi di valore finchè in pratica non lo si tocca ... quindi potrei avere uno struct Foo : IMyInterface e passarlo, e non verrà incassato.

La differenza diventa più evidente con le cose come le collezioni:

static void Foo(IEnumerable<IMyInterface> data) {} 

vs

static void Foo<T>(IEnumerable<T> data) 
    where T : IMyInterface {} 

Ora, dal momento che C# 3.0 non dispone di covarianza (tranne che per gli array), non posso passare uno List<Bar> quello superiore, anche se Bar : IMyInterface - ma posso con il secondo (implicito T = Bar).

+0

Le cose sono più interessanti con 'IEnumerator ', dal momento che alcuni degli enumeratori più comuni sono in realtà strutture. – supercat

+0

@supercat si, ma se si utilizza 'IEnumerable .GetEnumerator()', non è possibile utilizzarlo, ma userebbe quella versione per 'foreach' sulla cosa stessa –

+0

Se si utilizza' IEnumerator .GetEnumerator() 'questo è corretto.Se, comunque, si dice' using (var myEn = myList.GetEnumerator(); doSomething (ref myEn); 'e' doSomething' prende un 'IEnumer ator 'generico con restrizioni', può ricevere la cosa come una struttura (si noti che se non si fa attenzione a passare sempre con 'ref' la semantica sarà diversa dal tipo di interfaccia nuda; Inoltre, l'assegnazione del tipo generico può richiedere un'istantanea dello stato di enumerazione (funziona con alcuni enumeratori di tipo struct e non può funzionare con quelli di tipo classe). – supercat

0

Un'altra avvertenza da considerare in questo scenario è il fatto che l'utilizzo di "dove T: <% l'interfaccia di base o l'astrazione%>" può essere sovrautilizzato in generici, rendendo il tipo generico di natura non generica.

IE: Ricordare che isolando il metodo generico su IMyInterface, si sta isolando tale metodo solo su quei tipi che implementano IMyInterface. Quindi, se hai semplicemente scelto di usare IMyInterface basandosi su buoni principi OOP, ma hai un solo (o in alcuni casi un numero molto piccolo) di potenziale tipo ovunque che implementerà quell'interfaccia, allora hai sconfitto lo scopo di usare i generici. In tale circostanza, la prima opzione sarebbe migliore.

Utilizzare "where" sul tipo generico solo quando si dispone di una gamma più ampia di tipi che implementano effettivamente IMyInterface.

3

Un'altra differenza, utilizzando il metodo generico consente di specificare più interfacce che l'oggetto deve implementare:

class Dictionary<TKey,TVal> 
    where TKey: IComparable, IEnumerable 
    where TVal: IValue 
{ ... } 
1

Un altro sottile differenza è che non si può sovraccaricare un metodo solo sui vincoli (vincoli aren 't parte della firma del metodo):

Questo è illegale:

void MyMethod<T>(T value) where T : IMyInterface 
{ 
    //... 
} 

void MyMethod<T>(T value) where T : IMyInterface2 
{ 
    //... 
} 

, mentre questo è legale:

void MyMethod(IMyInterface value) 
{ 
    //... 
} 

void MyMethod(IMyInterface2 value) 
{ 
    //... 
} 
0

Ancora un'altra differenza per i metodi generici in generale (anche se non per il tuo esempio) è che se uno ha un metodo come T MungeThing<T>(T it) where T:IMungeable<T> e la classe Fnord implementa IMungeable<Fnord>, allora codice sarà in grado di dire : Fnord thing1, thing2; ... thing1 = MungeThing(thing2); e il compilatore saprà che MungeThing restituirà un Fnord anziché un'implementazione arbitraria di IMungable.