2015-10-30 7 views
9

Czytałem Albaharis' «C# 5.0 w pigułce», a ja napotkałem tego w sekcji Generics i to mówi się, że prawna:C# ogólne deklaracje własny przedstawieniu

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

A to oznaczało, nic dla mnie, chociaż uważnie przeczytałem cały rozdział. Nie mogłem tego zrozumieć nawet trochę.

Może ktoś proszę wyjaśnić to z jakiegoś zrozumiałego nazewnictwa, jak:

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

a scenariuszu aplikacji w rzeczywistym świecie, gdzie takie wykorzystanie jest właściwe i przydatne?

+1

może trochę pomocny chociaż nie jest duplikatem lub rozwiązanie kwestii: http: // stackover flow.com/questions/6618134/generic-class-z-zwykłymi -referencjami-type-zasięgów istnieje przykład w pytaniu ze zwierzętami i kaczkami – Thomas

+1

jeśli się nie mylę, to się nazywa "Ciekawy powtarzający się wzorzec szablonu" – wodzu

Odpowiedz

7

Oznacza to, że T musi odziedziczyć po .

Jest to typowy sposób tworzenia specyficznych dla danego typu metod lub właściwości lub parametrów w klasie bazowej, specyficznej dla rzeczywistego potomka.

Na przykład:

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

Jest to przydatne, gdy pracujesz z jakimś zewnętrznym biblioteki lub ram (które nie mogą lub nie chcą zmodyfikować). Na przykład, masz klasę User z tej biblioteki i definitywnie programista, który z niej skorzysta, zdefiniuje klasę CustomUser, która jest z niej dziedziczona (tylko po to, by dodać kilka niestandardowych pól). Wyobraźmy sobie również, że klasa User ma pewne odniesienia do innych użytkowników, na przykład: twórca i deletor (które oczywiście będą instancjami typu CustomUser). I w tym przypadku generyczne deklaracja samodniesienia deklaracja może pomóc bardzo dobrze. Będziemy przechodzić rodzaj potomka (CustomUser) jako parametr do podstawy (User) klasy, więc w User deklaracji klasy możemy ustawić typy twórcą i deletor dokładnie jak będą w przyszłości (CustomUser), więc nie odlew będzie być potrzebne:

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;} 
} 

Zastosowanie:

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