Это - расширение этого questionand, вероятно, мог бы даже быть дубликат некоторого другого вопроса (Если так, простите мне). Я вижу из MSDN, что дженерики обычно используются с наборами
Наиболее популярный способ использования универсальных классов с наборами как связанные списки, хеш-таблицы, стеки, очереди, деревья и так далее, где операции, такие как добавление и удаление объектов от набора выполняются почти таким же способом независимо от типа хранивших данных.
Примеры, которые я видел также, проверяют вышеупомянутый оператор.
Кто-то может дать допустимое использование дженериков в реальном сценарии, который не включает наборов?
Педантично, я думал о создании примера, который не включает наборы
public class Animal
{
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
Совершенно возможно иметь Dog
быть наследованным от Animal
(Принятие неуниверсальной версии Animal
)Dog:Animal
Поэтому Dog
Animal
Другим примером я думал, был a BankAccount
. Это может быть BankAccount
,BankAccount
. Это может очень хорошо быть Checking:BankAccount
и Savings:BankAccount
.
Там какие-либо лучшие практики должны определить, должны ли мы пойти с дженериками или с наследованием?
Вы, вероятно, получите более точные ответы, но тем не менее учтите следующее: Общие классы подразумевают отношение "из" между универсальным классом и класс параметра.
Все собаки животные, поэтому они разделяют определенные атрибуты / качества со всеми другими животными. Если вы используете наследование, то очень легко реализовать эти общие качества в классе Animal и добавить качества в дочерние классы.Но если вы реализуете его с помощью Animal (Of Dog)
, то:
Класс Животных (Of T)
. Собака должна быть связана с Животным родительско-дочерними отношениями. Animal (Of T)
. На самом деле это семейство классов с общим поведением, а не суперкласс. Вы теряете возможность иметь дело с животными вне класса Animal (Of T)
. Но вот как, ИМХО, вы должны думать о дженериках:
Подумайте о классе MedicalDoctor (Of T)
.
Veterinarian (Of T as Животное)
будет унаследован от MedicalDoctor (Of Animal)
.
DogVeterinarian
будет унаследован от Veterinarian (Of Dog)
.
Ключевой момент: общий класс и класс параметров не являются тесно связанными и взаимозависимыми, они сосуществуют.
Кстати, если вы хотите увидеть хорошее использование дженериков, не связанных с коллекциями, просто обратите внимание на делегатов, которые у нас есть: Action (Of T), Func (Of TResult), Comparison (Of T), EventHandler (Of TEventArgs), Конвертер (Of TSource, TResult) ... и интерфейсы: IEqualityComparer (Of T), IComparer (Of T) ...
Nullable
является универсальным, не является классом коллекции, и не может включать наследование, поскольку относится к struct
s.
Общие семейства делегатов довольно хороши - EventHandler
, Action <...]>
, Func <[...]>
- гораздо яснее, что такое Func
, а не какой-то специальный IdPredicate
или что-то еще.
Один известный пример реального мира находится в 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. Наследование представляет собой иерархические отношения между типами. Собака - это животное, золотистый ретривер - это одновременно и собака, и животное.
Используйте наследование в своей ситуации, пожалуйста.
Если класс может быть правильно описан другим классом [например, квадрат можно описать как прямоугольник], квадрат должен наследовать / расширяться от квеста прямоугольника. В противном случае вы попадете в беспорядок.
Используйте GenericClass, чтобы обозначить , что это объект GenericClass типа tType-s
Используйте Square: Rectangle, чтобы обозначить , что это квадрат, который также является прямоугольником
в ваш случай:
Используйте слово "собака: животное" для обозначения , это собака, которая также является животным
Вы путаете аспект наследования «есть-а» с аспектом «из» универсальных шаблонов.
Универсальные шаблоны подразумевают одинаковость операций между классами, а не только то, что операции существуют полиморфно.
В вашем примере с животными экземпляр Animal
не имеет метода WhoAmI
, тогда как экземпляр Dog: Animal
будет.