2009-11-29 18 views
16

Voglio solo confermare quello che ho capito su Generics in C#. Questo è venuto fuori in un paio di basi di codice in cui ho lavorato in cui una classe base generica viene utilizzata per creare istanze derivate dal tipo sicuro. Un esempio molto semplice di quello che sto parlando,C# Generici e polimorfismo: un ossimoro?

public class SomeClass<T> 
{ 
    public virtual void SomeMethod(){ } 
} 

public class DeriveFrom :SomeClass<string> 
{ 
    public override void SomeMethod() 
    { 
     base.SomeMethod(); 
    } 
} 

Il problema si presenta quando poi voglio usare le istanze derivate in modo polimorfico.

public class ClientCode 
{ 
    public void DoSomethingClienty() 
    { 
     Factory factory = new Factory(); 
     //Doesn't compile because SomeClass needs a type parameter! 
     SomeClass someInstance = factory.Create(); 

     someInstance.SomeMethod(); 
    } 
} 

Sembra che una volta che si introduce un generico in una gerarchia o interfaccia eredità, non è più possibile utilizzare quella famiglia di classi in modo polimorfico se non forse interno a se stesso. È vero?

+3

Mi sembra che SomeClass sia la classe base di DeriveFrom, non SomeClass, che è ciò che sembra credere che dovrebbe essere. – Thanatos

risposta

11

Per quanto posso vedere, il consumo di codice non richiede specifiche di classe generica (vale a dire, non dipende da cosa è T). Quindi, perché non introdurre l'interfaccia che implementerà SomeClass<T> e utilizzare l'istanza di questa interfaccia.

Es .:

public interface ISome 
{ 
    void SomeMethod(); 
} 

public class SomeClass<T>: ISome 
{ 
    public virtual void SomeMethod(){ } 
} 

public void DoSomethingClienty() 
{ 
    Factory factory = new Factory(); 
    ISome someInstance = factory.Create(); 

    someInstance.SomeMethod(); 
} 

Ora, sottoclassi di SomeClass<T> può operare in modo diverso su diversi T s, ma il codice che consumano non cambierà.

+0

Penso che questa sia la soluzione migliore. Estendiamo solo l'interfaccia che interessa al cliente e nascondiamo i generici sotto il cofano. –

9

Penso che tu abbia frainteso il punto di generici. Generics consente di generalizzare una classe che richiede un tipo, ma non si cura particolarmente di quale tipo sia. Ad esempio, un List<string> è un elenco di stringhe, ma cosa sarebbe un List? È un concetto piuttosto inutile avere una lista di niente.

Ogni classe specializzata (ad esempio, List<string>) è il proprio tipo distinto e il compilatore lo considera come tale. È possibile per ottenere il tipo generico stesso (typeof(List<>) per esempio), ma nella maggior parte dei casi è inutile, e certamente non è possibile creare un'istanza di esso.

2

Preferirei usare la classe astratta per agire come base di tutti i tipi generici.

public abstract class SomeClass 
{ 
    public abstract void SomeMethod(); 
} 

public class SomeClass<T> : SomeClass 
{ 
    public override void SomeMethod() { }    
} 

public class DeriveFrom<String> : SomeClass<String> 
{ 
    public override void SomeMethod() { base.SomeMethod(); }    
} 
+0

In effetti, ma come funzionerebbe 'Factory' qui? –

+0

Non hanno idea dell'esecuzione di "Factory" menzionata da Nick. Ma sono abbastanza sicuro, può benissimo essere implementato nell'astratto "SomeClass" non generico. –

+0

@Amby Ma cosa faresti se il vuoto astratto SomeMethod() ha un tipo di ritorno di T e un parametro di tipo T? Quindi non puoi nascondere i generici sotto la tua classe astratta non generica. Gradirei una risposta o un suggerimento da parte tua :) – Elisabeth

0

Penso che quello che stai cercando è:

 SomeClass(of someType) someInstance = factory(of someType).Create(); 
or maybe 
    SomeClass(of someType) someInstance = factory.Create(of someType)(); 
or 
    SomeClass(of someType) someInstance = factory.Create(); 

E 'possibile avere un insieme di classi di fabbrica per creare diverse classi generiche, o di avere una fabbrica con un parametro di tipo generico per indica quale tipo generico deve creare (notare che in entrambi i casi, il parametro type è il parametro type per la classe generica, piuttosto che la classe generica stessa). È anche possibile avere una factory progettata per restituire istanze di una particolare forma di un tipo generico.

0

Sembra che una volta che si introduce un generico in una gerarchia o interfaccia eredità, non è più possibile utilizzare quella famiglia di classi in modo polimorfico

corretta, è abbastanza simile a questa situazione:

class StringContainer 
{ 
} 

class IntContainer 
{ 
} 

StringContainer container = new IntContainer(); // fails to compile 

ma si potrebbe fare questo:

class Container 
{ 
} 

class Container<T> : Container 
{ 
} 

Container container = new Container<String>(); // OK 
0
public Class ReflectionReport<T> 
{ 
    // This class uses Reflection to produce column-based grids for reporting. 
    // However, you need a type in order to know what the columns are. 
} 

... so, in another class you have... 

public Class ReflectionReportProject { 
    ReflectionReport<Thang> thangReport = new ReflectionReport<Thang>(); 
    ReflectionReport<Thong> thongReport = new ReflectionReport<Thong>(); 



    ... some more stuff ... 

    // Now, you want to pass off all of the reports to be processed at one time.... 
    public ReflectionReport<????>[] ProvideReports() 
    { 
     return new ReflectionReport<????>[] { thangReport, thongReport } ; 
    } 
} 
+2

Quanto sopra è un esempio di come il polimorfismo con i generici sarebbe davvero utile. – user1126454