2015-10-30 5 views
9

Я читал Albaharis' C# 5.0 в ореховой скорлупе ", и я столкнулся с этим в разделе дженерик, и он сказал, чтобы быть законными:с # родовой автореферентной декларацией

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

И это означало, ничего для меня, хотя я внимательно прочитал всю главу. Я даже не мог понять этого.

Может кто-то пожалуйста, объясните это с каким-то понятным наименованием, как:

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

И сценарий приложений реального мира, где такое использованием является целесообразным и полезным?

+1

может быть немного полезным, хотя это не является дубликатом или решение вопроса: HTTP: // stackover flow.com/questions/6618134/generic-class-with-self-referencing-type-constraint есть пример в вопросе с животными и утками – Thomas

+1

, если я не ошибаюсь, это называется «Любопытно повторяющийся шаблон шаблона», – wodzu

ответ

7

Это означает, что T должен наследовать от Person<T>.

Это типичный способ создания специфичных для типа методов или свойств или параметров в базовом классе, специфичном для фактического потомка.

Например:

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

Это полезно, когда вы работаете с какой-либо внешней библиотеки или рамки (которые вы не можете или не хотите изменять). Например, у вас есть класс User из этой библиотеки, и определенно разработчик, который будет его использовать, определит класс CustomUser, который унаследован от него (только для добавления некоторых настраиваемых полей). Также давайте представим, что класс User имеет некоторые ссылки на других пользователей, например: создатель и удаление (которые, очевидно, будут экземплярами типа CustomUser). И в этом случае общий код саморегуляторной декларации может очень помочь. Мы будем проходить тип потомка (CustomUser) в качестве параметра базового (User) класса, поэтому в User объявлении класса мы можем установить типы создателя и Deletor точно, как они будут в будущем (CustomUser), так не отливку будет необходимы:

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

Использование:

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