2015-10-30 15 views
9

che ho letto Albaharis' 'C# 5.0 in a Nutshell' e ho incontrato questo nella sezione Generics e si dice che sia legale:C# generiche dichiarazioni autoreferenziali

class Bar<T> where T : Bar<T> { ... } 

E significava niente per me, anche se ho letto attentamente l'intero capitolo. Non riuscivo a capirlo nemmeno un po '.

Qualcuno può spiegare con una certa denominazione comprensibile, come:

class Person<T> where T : Person<T> { ... } 

E uno scenario di mondo reale applicazione dove tale uso è opportuno e utile?

+1

forse un po 'utile anche se non è un duplicato o una soluzione al problema: http: // stackover flow.com/questions/6618134/generic-class-with-self-referencing-type-constraint c'è un esempio nella domanda con animali e anatre – Thomas

+1

se non sbaglio, questo è chiamato il "modello di modello curiosamente ricorrente" – wodzu

risposta

7

Significa che T deve ereditare da Person<T>.

Questo è un modo tipico per creare metodi specifici del tipo o proprietà o parametri nella classe base, specifici per il discendente effettivo.

Per esempio:

public abstract class Base<T> where T : Base<T>, new() 
{ 
    public static T Create() 
    { 
     var instance = new T(); 
     instance.Configure(42); 
     return instance; 
    } 

    protected abstract void Configure(int value); 
} 

public class Actual : Base<Actual> 
{ 
    protected override void Configure(int value) { ... } 
} 

... 

Actual a = Actual.Create(); // Create is defined in Base, but returns Actual 
0

E 'utile, quando si lavora con una certa libreria esterna o di un quadro (che non si può o non si desidera modificare). Ad esempio, hai la classe User da questa libreria e sicuramente lo sviluppatore, che la utilizzerà, definirà la classe CustomUser, che viene ereditata da essa (solo per aggiungere alcuni campi personalizzati). Immaginiamo anche che la classe User abbia alcuni riferimenti a un altro utente, ad esempio: creatore e deletore (che ovviamente saranno istanze del tipo CustomUser). E in questo caso la dichiarazione di auto-referenziamento generica può essere di grande aiuto. Passeremo tipo di discendente (CustomUser) come parametro di base (User) di classe, così User dichiarazione di classe siamo in grado di impostare i tipi di creatore e Deletor esattamente in quanto saranno in futuro (CustomUser), in modo da nessuna fusione volontà essere necessario:

public class User<TCustomUser> where TCustomUser : User<TCustomUser> 
{ 
    public TCustomUser creator {get;set;} 
    public TCustomUser deletor {get;set;} 

    //not convenient variant, without generic approach 
    //public User creator {get;set;} 
    //public User deletor {get;set;}  
} 

public class CustomUser : User<CustomUser> 
{ 
    //custom fields: 
    public string City {get;set;} 
    public int Age {get;set;} 
} 

Usage:

CustomUser customUser = getUserFromSomeWhere(); 
//we can do this 
var creatorsAge = customUser.creator.Age; 
//without generic approach: 
//var creatorsAge = ((CustomUser)customUser.creator).Age;