2013-10-10 18 views
6

Mi sono imbattuto nel seguente codice, il tutto in un singolo file/classe. Tralascio i dettagli, è il costrutto a cui sono interessato. Perché ci sono due dichiarazioni diverse per la stessa classe e in che modo sono differenti? Qual è lo scopo della sintassi per la seconda dichiarazione?Che cosa sta facendo questo costrutto C# e perché? MyClass <TMyClass>: MyClass dove

public abstract class MyClass 
{ 
    ... 
} 

public abstract class MyClass<TMyClass> : MyClass 
    where TMyClass: MyClass<TMyClass> 
{ 
    ... 
} 

risposta

2
public abstract class MyClass<TMyClass> : MyClass 
    where TMyClass: MyClass<TMyClass> 
{ 
    ... 
} 

è una classe che eredita da MyClass, e ci vuole un tipo generico, che deve ereditare da MyClass<TMyClass>


Ecco un esempio più semplice della stessa cosa per voi

public static void Main() 
    { 
     MyClass<Myclass> other = new MyClass<Myclass>(new Myclass()); 
     List<int> intlist = new List<int>(); 
    } 

    public class Myclass 
    { 
     public Myclass() 
     { 
     } 
     public int i { get; set; } 
    } 

    public class MyClass<T> where T : Myclass 
    { 
     T value; 
     public MyClass(T val) 
     { 
      value = val; 
     } 
    } 
} 
+0

Quindi questa seconda classe ha lo stesso nome ma poiché ha un parametro di tipo generico, è una classe diversa? Come potrei discendere da questo? –

+0

@SteveWash e sì, ci sono diverse classi –

+0

-1: l'esempio "semplice" è ** molto ** diverso da quello che fa il codice originale. Etichettarlo "la stessa cosa" è sia sbagliato che fuorviante. – Jon

3

MyClass - Una classe astratta denominata MyClass.

MyClass<TMyClass> : MyClass - Una classe generica astratta denominata MyClass<> ma con un tipo generico denominato TMyClass.

Se si rinominano i tipi, sarà più facile da vedere:

public abstract class MyBaseClass 
{ 
    ... 
} 

public abstract class MyClass<T> : MyBaseClass 
    where T: MyClass<T> 
{ 
    ... 
} 
3

tipi con diversi arity generica (cioè il numero di parametri di tipo generico, che può essere zero o più) sono considerati completamente estranei da la lingua e può avere lo stesso nome.

Ciò significa che è possibile avere classi Foo, Foo<T> e Foo<T,U> allo stesso tempo; la sintassi consentirà al compilatore di determinare a quale ti stai riferendo. Si può vedere che questo accada nel quadro di base che comprende Action, Action<T> ecc

Il "ricorsivo" costruire class C<T> where T: C<T> (l'eredità da una non generica C non cambia nulla così ho rimosso) è il C# su ciò che è chiamato Curiously Recurring Template Pattern (CRTP) in C++. Eric Lippert has covered this subject molto bene in un post sul blog, dove la conclusione è che si dovrebbe pensare più di due volte prima di implementare questo - ci sono problemi che può risolvere, ma la soluzione ha anche un prezzo.

0

È un classico idioma, il modello di modello Curiosamente ricorrente, fatto in C#.

E significa che il modello può essere utilizzato solo nel seguente modo:

class Foo : MyClass<Foo> 
{ 
} 

In questa costruzione, Foo eredita MyClass<Foo> che eredita MyClass.

Questo ha alcuni vantaggi, ma ho dimenticato quale.

-1
public abstract class MyClass<TMyClass> : MyClass 
     where TMyClass: MyClass<TMyClass> 
{ 
     ... 
} 

La prima cosa da sottolineare è che questa è una classe astratta che si eredita da un'altra classe astratta. In altre parole, questa è una classe che non può essere istanziata (senza un'altra classe che ne eredita), ma sta usando l'ereditarietà per derivare la funzionalità da un'altra classe astratta (che va bene).

La seconda cosa da sottolineare, è che questa è una classe Template (o una classe generica come la chiamano in C#) che accetta il tipo in esso. Vorrei ridurlo a T come una convenzione in modo che T sia sempre un modello, anche se è completamente a te decidere ciò che chiami.

Infine c'è un vincolo su questo che è un po 'strano. Si dice che, non importa cosa, il compilatore non permetterà alcun tipo di classe per essere passato come un tipo di modello, a meno che non eredita da (da qualche parte lungo la catena di ereditarietà)

MyClass<TMyClass> 

Questo è mostrato nella seguente riga

where TMyClass: MyClass<TMyClass> 

fondamentalmente questo impedisce a chiunque di passare in un oggetto che non segue questa regola.

Ciò che è un po 'strano è che i vincoli dicono all'implementatore che non può essere un modello a meno che il tipo passato da template sia in realtà un tipo di se stesso. Tu come designer di questa classe (o implementatore) devi decidere se questo è un design saggio, anche se questo di per sé sembra un po 'strano.

Problemi correlati