Нужна помощь, чтобы понять этот родовой класс C # - PullRequest
4 голосов
/ 06 декабря 2011

Я изучаю Nhibernate 3.0.В одном из примеров кода он создает абстрактный базовый класс сущности:

public abstract class Entity<T> where T : Entity<T>

, а затем заставляет сущность Customer наследовать от базового класса Entity:

public class Customer : Entity<Customer>

Я понимаю, что это абстрактный обобщенный класс, и он использует ключевое слово where, чтобы убедиться, что тип T равен Entity<T>, вот где я запутался.

Customer наследуется от "Entity<Customer> ", это" Entity<Customer> "принимает" Customer "как T, но это Customer не является" Entity<T> ".

Пожалуйста, помогите мне понять это, я 'Я действительно смущен этим родовым классом.

Ответы [ 4 ]

8 голосов
/ 06 декабря 2011

Вы сказали

Клиент наследует от «Entity», этот «Entity» принимает «Customer» в качестве T, но этот клиент не "сущность"

Это не имеет никакого смысла, потому что это то, что означает наследование. Он устанавливает отношения "есть". Так что на самом деле Customer это Entity

Извините, что был основан на коде с удаленными шаблонами, потому что он не был в блоке кода.

Тот же принцип все еще действует. Это немного сбивает с толку, потому что похоже, что это рекурсивное определение, но это не так.

Думайте об этом как о Customer, унаследованном от Entity Просто случается, что есть методы или поля, которые зависят от самого общего параметра, например Customer. Я не знаком с NHibernate, поэтому я не знаю, как выглядит остальная часть Entity<T>, но я думаю, что у него есть некоторые методы, которые используют его собственный тип в качестве универсального параметра.

Скажем, например, у него есть метод с именем

public IEnumerable<T> GetEntities() 

, который вернул список своих экземпляров. Этот метод должен возвращать конкретный тип, а не базовый тип. Таким образом, в классе Customer этот метод будет

public IEnumerable<Customer> GetEntities<Customer>() 

Если бы он не имел универсального параметра, он мог бы только вернуть IEnumerable<Entity>

Это всего лишь пример того, как это можно использовать, я не знаю, как это на самом деле используется.

1 голос
/ 06 декабря 2011

Будет больше смысла, если учесть, какие операции пытается выполнить базовый класс Entity. Я также не знаком с nhibernate, но я думаю, что один из методов может быть чем-то похожим на метод Save (). Таким образом, любой создаваемый вами класс, который наследуется от класса Entity, будет наследовать метод Save (), не позволяя вам переписывать его для каждого создаваемого вами бизнес-объекта. Однако класс сущности Base должен знать, какой тип объекта вы пытаетесь сохранить. Он может использовать рефлексию, но здесь он использует дженерики, чтобы вы могли сказать ему, что это за класс, который наследует Entity.

Дело в том, что когда 20 различных классов наследуют от базового класса, этот базовый класс на самом деле не знает, кто использует его функциональные возможности. Это способ сообщить базовому классу, что «Клиент» использует свои методы, чтобы он мог точно соответствовать потребностям «Клиента».

0 голосов
/ 06 декабря 2011

Пример, показывающий, как можно использовать такое наследование:

class Creatable<T> where T:Creatable<T>, new()
{
 pulibc static T Create()
 {
   T t = new T(); // we know to call new due new() constraint
   t.FinishCreation(); // we know that T implements this method due Creatable<T> constraint
   return t;
 }
 public void SomeOtherUsefulsOperation(){};
 protected virtual void FinishCreation(){};
}

class Child:Creatable<Child>
{
 public Child(){};
 protected override void FinishCreation(){/*do something special for this type */};}
}

// somewhere else
void DoSomething<T>() where T:Creatable<T>
{ 
 T item = Creatable<T>.Create();
 item.SomeOtherUsefulOperation();
}
0 голосов
/ 06 декабря 2011

Предложение where указывает условие, что тип, который будет заменен на T, должен подчиняться.Таким образом, если типом является Customer, как в Entity<Customer> во второй строке кода, то условие будет Customer : Entity<Customer> ... т.е. Customer должно быть подклассом Entity<Customer>, иначе есть компиляцияошибка.И действительно, это так объявлено снова во второй строке кода.

Применяя это к тому, что вы написали:

этот "Entity<Customer>" воспринимает "Customer" как T

Вот как я бы сказал: Entity<Customer> - это экземпляр Entity<T> с Customer вместо T.T это просто заполнитель для некоторого типа;это тип параметр .

, но этот клиент не "Entity<T>"

Мы могли бы также написать объявление абстрактного метода с помощью SomeType вместо T.Условие состоит в том, что для создания экземпляра Entity< SomeType >, SomeType должен быть подклассом Entity< SomeType >.Подставляя Customer для SomeType , это говорит о том, что Customer должен быть подклассом Entity<Customer>, и это так.

Если вы понимаете, что T это просто параметр , и вместо него Customer подставляется *1056*, тогда я не понимаю, почему вы говорите, что «этот клиент не является« Entity<T> »», так как Customer : Entity<Customer> объявляетэто будет именно так (с Customer вместо T в каждом случае в определении Entity<T>).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...