В объектно-ориентированных языках, которые поддерживают параметры универсального типа (также называемые шаблонами классов и параметрическим полиморфизмом, хотя, конечно, каждое имя несет в себе различные коннотации), часто можно указать ограничение типа для параметра типа, например, для его наследования. из другого типа. Например, это синтаксис в C #:
//for classes:
class ExampleClass<T> where T : I1 {
}
//for methods:
S ExampleMethod<S>(S value) where S : I2 {
...
}
Каковы причины использовать фактические типы интерфейсов над типами, ограниченными этими интерфейсами? Например, каковы причины создания подписи метода I2 ExampleMethod(I2 value)
?
object-oriented
type-systems
generics
GregRos
источник
источник
ref
параметров типа значения может фактически изменить тип значения.Ответы:
Использование параметрической версии дает
В качестве случайного примера предположим, что у нас есть метод, который вычисляет корни квадратного уравнения
И затем вы хотите, чтобы он работал на другие виды числа, как вещи, помимо
int
. Вы можете написать что-то вродеПроблема в том, что это не говорит о том, что вы хотите. Это говорит
Мы не можем сделать что - то вроде ,
int sol = solve(a, b, c)
еслиa
,b
иc
этоint
потому , что мы не знаем , что этот метод будет возвращатьint
в конце концов! Это приводит к некоторым неловким танцам с удручением и молитвой, если мы хотим использовать решение в более широком выражении.Внутри функции кто-то может передать нам число с плавающей запятой, бигинт и градусы, и нам придется сложить и умножить их вместе. Мы хотели бы статически отклонить это, потому что операции между этими 3 классами будут бессмысленными. Степени - мод 360, так что не будет случая,
a.plus(b) = b.plus(a)
когда возникнут подобные забавы.Если мы используем параметрический полиморфизм с подтипами, мы можем исключить все это, потому что наш тип фактически говорит о том, что мы имеем в виду
Или словами «Если вы дадите мне какой-то тип, который является числом, я могу решить уравнения с этими коэффициентами».
Это встречается и во многих других местах. Другой хороший источник примеров являются функции, абстрактной над каким - то контейнером, ала
reverse
,sort
,map
и т.д.источник
Num<int>
), как дополнительный аргумент. Вы всегда можете реализовать интерфейс для любого типа через делегирование. Это, по сути, классы типов в Haskell, за исключением гораздо более утомительного использования, так как вы должны явно обойти интерфейс.Потому что это то, что вам нужно ...
две решительно разные подписи. Первый принимает любой тип, реализующий интерфейс, и единственная гарантия, которую он дает, состоит в том, что возвращаемое значение удовлетворяет интерфейсу.
Второй принимает любой тип, реализующий интерфейс, и гарантирует, что он снова вернет хотя бы этот тип (а не что-то, что удовлетворяет менее ограничивающему интерфейсу).
Иногда вам нужна более слабая гарантия. Иногда хочется более сильного.
источник
Or
которая принимает дваParser
объекта (абстрактный базовый класс, но принцип работает) и возвращает новыйParser
(но с другим типом). Конечный пользователь не должен знать или заботиться о том, какой конкретно тип.IEnumerable<T>
, возвращает другое,IEnumerable<T>
которое, например, фактически являетсяOrderedEnumerable<T>
)Использование ограниченных обобщений для параметров метода может позволить методу очень возвращать свой тип возврата, основанный на типе передаваемой вещи. В .NET они также могут иметь дополнительные преимущества. Из их:
Метод, который принимает ограниченный родовой тип как параметр
ref
или,out
может передавать переменную, которая удовлетворяет ограничению; напротив, неуниверсальный метод с параметром типа интерфейса был бы ограничен принятием переменных этого точного типа интерфейса.Метод с параметром универсального типа T может принимать универсальные коллекции T. Метод, который принимает
IList<T> where T:IAnimal
, сможет принять aList<SiameseCat>
, но метод, который хотелIList<Animal>
, не сможет это сделать.Ограничение может иногда указывать интерфейс в терминах общего типа, например
where T:IComparable<T>
.Структура, которая реализует интерфейс, может быть сохранена как тип значения при передаче в метод, принимающий ограниченный универсальный параметр, но должна быть помещена в коробку при передаче как тип интерфейса. Это может оказать огромное влияние на скорость.
Универсальный параметр может иметь несколько ограничений, в то время как нет другого способа указать параметр «некоторого типа, который реализует как IFoo, так и IBar». Иногда это может быть обоюдоострый меч, так как код, получивший параметр типа,
IFoo
будет очень трудно передать его такому методу, ожидающему универсальный тип с двойным ограничением, даже если рассматриваемый экземпляр будет удовлетворять всем ограничениям.Если в конкретной ситуации не будет никакого преимущества в использовании универсального, просто примите параметр типа интерфейса. Использование универсального типа заставит систему типов и JITter выполнять дополнительную работу, поэтому, если нет никакой выгоды, никто не должен этого делать. С другой стороны, очень часто применяется, по крайней мере, одно из указанных выше преимуществ.
источник