Я не понимаю следующее объявление класса:
public abstract class Class1 <TDomainServiceContract, TDomainService>
{
...
}
Я знаю, что TDomainServiceContract
и TDomainService
, но почему они используются между символами <
и >
?
Я не понимаю следующее объявление класса:
public abstract class Class1 <TDomainServiceContract, TDomainService>
{
...
}
Я знаю, что TDomainServiceContract
и TDomainService
, но почему они используются между символами <
и >
?
Параметры между <
и >
являются параметрами типового типа. Generics, на очень высоком уровне, позволяют вам создать класс, который не зависит от конкретного типа одного или нескольких параметров, свойств или методов. Это немного сложно объяснить словами, но наиболее распространенное использование дженериков содержится в коллекциях.
До генериков большинство разработчиков использовали такие вещи, как ArrayList
, чтобы отслеживать коллекции объектов. Недостатком этого была безопасность; потому что вы можете поместить любой object
в ArrayList
, а это значит, что вам нужно было вернуть объект обратно к ожидаемому типу (делая код менее чистым), и у вас не было ничего, что помешало бы вам добавить что-то, что не было такого типа (т.е. я мог бы иметь ArrayList
, что я мог бы содержать только объекты string
, но я мог бы - случайно - поставить int
или DbConnection
и т.д.), а вы 'd никогда не узнайте, пока вы не закончите выполнение.
ArrayList myStrings = new ArrayList();
myStrings.Add("foo");
myStrings.Add("bar");
myStrings.Add(1); // uh-oh, this isn't going to turn out well...
string string1 = (string)myStrings[0];
string string2 = (string)myStrings[1];
string string3 = (string)myStrings[2]; // this will compile fine but fail at
// runtime since myStrings[2] is an int,
// not a string
После введения дженериков мы получили класс List<T>
. Это один класс, который принимает один общий аргумент типа, а именно тип объектов, которые вы ожидаете от списка. Таким образом, я могу иметь List<string>
или List<int>
, которые будут: a) не требовать кастинга, так как индексы возвращают string
и int
соответственно, и b) будут безопасны во время компиляции, так как я знаю, что в эти списки могут быть помещены только те string
или int
(опять же, соответственно).
List<string> myStrings = new List<string>();
myStrings.Add("foo");
myStrings.Add("bar");
myStrings.Add(1); // this will not compile, as an int is not a string
Точка дженериков состоит в том, чтобы сказать, что вас не волнует, каков фактический тип объекта, с которым вы работаете, но потребитель вашего класса может. Другими словами, механики того, как список может хранить string
, int
, a DbConnection
и т.д., Являются идентичными, но generics делают это так, чтобы информация этого типа, поступающая от потребителя вашего класса, t потеряно в вашей абстракции.
Они означают, что класс является общим классом, в вашем примере с ограничениями на типы - оба должны быть DomainContext
или классы, которые вытекают из него.
Подробнее см. Введение в Generics (Руководство по программированию на С#) в MSDN.
Это указывает на то, что при использовании класса вы можете предоставить любой тип для TDomainServiceContract
, который реализует DomainContext
. Поэтому допустим такой класс:
public class MyDomainContext : DomainContext
{
...
}
Я могу использовать этот класс при создании нового Class1
следующим образом:
var o = new Class1<MyDomainContext, MyDomainService>();
поскольку он реализует DomainContext
. То же самое относится к TDomainService
.
Еще один способ взглянуть на это - в более простом примере. Например:
List<String> myStrings = new List<String>();
myStrings.Add("One");
//Etc.
В этом примере вы просто определяете, какой тип данных вы будете использовать с классом. Аналогичная ситуация в вашем примере.