Generics vs inheritance (если не задействованы классы коллекции)
Это расширение этого вопроса и, возможно, даже может быть дубликатом какого-то другого вопроса (если так, пожалуйста, простите меня). я см. Из MSDN, что общие файлы обычно используются с коллекциями
Наиболее распространенное использование для общих классы с коллекциями вроде связанные списки, хеш-таблицы, стеки, очереди, деревья и т.д., где такие операции, как добавление и удаление предметы из коллекции выполняются почти так же независимо от типа данных, являющихся сохранены.
Примеры, которые я видел, также подтверждают приведенное выше утверждение.
Может ли кто-нибудь дать действительное использование дженериков в реальном сценарии, который не содержит каких-либо коллекций?
Педантично, я думал о том, чтобы сделать пример, который не включает коллекции
public class Animal<T>
{
public void Speak()
{
Console.WriteLine("I am an Animal and my type is " + typeof(T).ToString());
}
public void Eat()
{
//Eat food
}
}
public class Dog
{
public void WhoAmI()
{
Console.WriteLine(this.GetType().ToString());
}
}
и "Animal типа Собака" будет
Animal<Dog> magic = new Animal<Dog>();
Вполне возможно, что Dog
наследуется от Animal
(Предполагая не общий вариант Animal
) Dog:Animal
Поэтому Dog
является Animal
Другим примером, о котором я думал, был BankAccount
. Это может быть BankAccount<Checking>
, BankAccount<Savings>
. Это может быть наилучшим образом Checking:BankAccount
и Savings:BankAccount
.
Есть ли какие-либо рекомендации, чтобы определить, следует ли нам идти с дженериками или с наследованием?
Ответы
Ответ 1
Вероятно, вы получите несколько лучших ответов, но тем временем подумайте:
Общие классы подразумевают "из" отношения между общим классом и классом параметров.
Все собаки - животные, поэтому они имеют определенные атрибуты/качества со всеми другими животными. Если вы используете наследование, то очень легко реализовать эти общие качества в классе Animal и добавить качества в децентрализованные классы. Но если вы реализуете его с помощью Animal (Of Dog)
, то:
- Ваш класс Dog не является полностью полноценной собакой/животным сам по себе, так как его "свойства животных" содержатся в классе
Animal (Of T)
. Собака должна быть привязана к животному в отношениях между родителем и ребенком.
- Класс вашего животных на самом деле не является животным: вы не можете создать какой-либо метод, который принимает Животные как аргумент, потому что вы не можете ссылаться на глобализованный класс
Animal (Of T)
. На самом деле это семейство классов с общим поведением, а не суперкласс. Вы теряете преимущество общения с животными вне класса Animal (Of T)
.
Но вот как, ИМХО, вы должны думать о дженериках:
Подумайте о классе MedicalDoctor(Of T)
.
Veterinarian(Of T as Animal)
будет наследовать от MedicalDoctor(Of Animal)
.
DogVeterinarian
наследует от Veterinarian(Of Dog)
.
Ключевой момент: общий класс и класс параметров не являются тесно связанными и созависимыми, они сосуществуют.
Кстати, если вы хотите увидеть какое-то хорошее использование коллекций, не относящееся к коллекции, просто заметите, что у нас есть делегаты: Action (Of T), Func (Of TResult), Сравнение (Of T), EventHandler (Of TEventArgs), Конвертер (из TSource, TResult)... и интерфейсы: IEqualityComparer (Of T), IComparer (Of T)...
Ответ 2
Один пример реального мира, который знает, находится в WCF Channel Factory.
На странице:
public sealed class Test
{
static void Main()
{
// Code not shown.
}
public void Run()
{
// This code is written by an application developer.
// Create a channel factory.
BasicHttpBinding myBinding = new BasicHttpBinding();
EndpointAddress myEndpoint = new EndpointAddress("http://localhost/MathService/Ep1");
ChannelFactory<IMath> myChannelFactory = new ChannelFactory<IMath>(myBinding, myEndpoint);
// Create a channel.
IMath wcfClient1 = myChannelFactory.CreateChannel();
double s = wcfClient1.Add(3, 39);
Console.WriteLine(s.ToString());
((IClientChannel)wcfClient1).Close();
// Create another channel.
IMath wcfClient2 = myChannelFactory.CreateChannel();
s = wcfClient2.Add(15, 27);
Console.WriteLine(s.ToString());
((IClientChannel)wcfClient2).Close();
myChannelFactory.Close();
}
}
То, что я думаю о дженериках, состоит в том, что они представляют собой тип, на который действует класс. Например, ChannelFactory строит заводы типа T. Inheritance представляет иерархическую взаимосвязь между типами. Собака - это животное, золотистый ретривер - собака и животное.
Ответ 3
Nullable<T>
является общим, не является классом коллекции и не может включать наследование, поскольку он относится к struct
s.
Общие семейства делегатов довольно приятны - EventHandler<T>
, Action<...]>
, Func<[...]>
- гораздо яснее, чем Func<int,bool>
, а не какой-либо пользовательский IdPredicate
или что-то еще.
Ответ 4
Вы вводите в заблуждение аспект наследования "is-a" с "аспектом" дженериков.
Дженерики подразумевают однотипность операций между классами, а не только то, что операции существуют полиморфно.
В примере Animal экземпляр Animal<Dog>
не имеет метода WhoAmI
, тогда как экземпляр Dog : Animal
будет.
Ответ 5
Используйте наследование в своей ситуации, пожалуйста.
Если класс может быть правильно описан другим классом [например, квадрат может быть описан как прямоугольник], квадрат должен наследовать/расширяться из квеста прямоугольника. В противном случае вы попадаете в беспорядок.
Используйте GenericClass для обозначения this is a GenericClass object of tType-s
Используйте Square: Rectangle, чтобы обозначить this is a Square, which is also a Rectangle
В вашем случае:
Используйте Dog: Animal для обозначения this is a Dog, which is also an Animal