День сто девяносто третий. #ЗаметкиНаПолях
Заметки по обобщённым типам
2. Вывод типа из аргументов методов
Рассмотрим следующий код:
Хотя вывод типов применяется только к методам, его можно использовать для более простого создания экземпляров обобщённых типов. Например, рассмотрим семейство типов
Они выглядят бессмысленно тривиальными, но позволяют использовать вывод типа. То есть, вместо этого:
Разработчики языка постоянно работают над усовершенствованием процесса вывода типа, и порой логика вывода становится достаточно сложной из-за наследований, перегрузок и необязательных параметров. Поэтому надо понимать, что иногда на практике выведение типа может приводить к неожиданным результатам. Например, если вам нужен
Источник: Jon Skeet “C# In Depth”. 4th ed – Manning Publications Co, 2019. Глава 2.
Заметки по обобщённым типам
2. Вывод типа из аргументов методов
Рассмотрим следующий код:
public static List<T> CopyAtMost<T>(List<T> input, int elements)…
{
return input.Take(elements).ToList();
}
var numbers = new List<int>() { 1, 2, 3, 4 };Вам нужен аргумент типа для вызова
var firstTwo = CopyAtMost<int>(numbers, 2);
CopyAtMost
, потому что метод имеет параметр типа. Но вам не нужно указывать этот тип аргумента явно. Можно переписать этот код следующим образом:var firstTwo = CopyAtMost(numbers, 2);Это точно такой же вызов метода с точки зрения IL, который сгенерирует компилятор. Но аргумент типа int указывать не нужно, компилятор сделал это для вас на основе аргумента для первого параметра метода. Вы используете аргумент
List<int>
в качестве значения параметра типа List<T>
, поэтому T
должно быть int
. Вывод типа может использовать только аргументы, передаваемые методу, но не тип результата.Хотя вывод типов применяется только к методам, его можно использовать для более простого создания экземпляров обобщённых типов. Например, рассмотрим семейство типов
Tuple
, состоящее из необобщённого статического класса Tuple
и нескольких обобщённых классов: Tuple<T1>
, Tuple<T1, T2>
, Tuple<T1, T2, T3>
и так далее (до 8). Статический класс имеет набор перегруженных фабричных методов Create
:public static Tuple<T1> Create<T1>(T1 item1)и так далее…
{
return new Tuple<T1>(item1);
}
public static Tuple<T1, T2> Create<T1, T2>(T1 item1, T2 item2)
{
return new Tuple<T1, T2>(item1, item2);
}
Они выглядят бессмысленно тривиальными, но позволяют использовать вывод типа. То есть, вместо этого:
new Tuple<int, string, int>(10, "x", 20)Можно написать:
Tuple.Create(10, "x", 20)Это мощная техника, о которой полезно знать; как правило, её просто реализовать и она может сделать работу с обобщённым кодом намного приятнее.
Разработчики языка постоянно работают над усовершенствованием процесса вывода типа, и порой логика вывода становится достаточно сложной из-за наследований, перегрузок и необязательных параметров. Поэтому надо понимать, что иногда на практике выведение типа может приводить к неожиданным результатам. Например, если вам нужен
Tuple<int, object, int>
, то из предыдущего вызова Tuple.Create(10, "x", 20)
вы его не получите. В этом случае можно использовать либо new Tuple<int, object, int>(10, "x", 20)
, либо приведение типа Tuple.Create(10, (object)"x", 20)
. Аналогично с null: Tuple.Create(null, 50)
завершится неудачей, но Tuple.Create((string) null, 50)
сработает.Источник: Jon Skeet “C# In Depth”. 4th ed – Manning Publications Co, 2019. Глава 2.