Методы и анонимные типы
-
11-07-2019 - |
Вопрос
Я знаю, что вы не можете возвращать анонимные типы из методов, но мне интересно, как метод расширения Select возвращает анонимный тип.Это просто трюк компилятора?
Редактировать
Предположим, L — список.Как это работает?
L.Select(s => new { Name = s })
Тип возвращаемого значения — IEnumerable<'a>, где 'a = new {String Name}.
Решение
Тип фактически определяется звонящий, поэтому он находится в области действия вызывающей функции, аккуратно избегая проблемы «возврата» анонимного типа.
Это достигается путем вывода универсального типа.Подпись для Выбирать является Select<Tsource, TResult>(IEnumerable<TSource>, Func<TSource, TResult>
.А IEnumerable<TSource>
это, очевидно, исходная коллекция.А Func<Tsource, TResult>
Функция преобразования — это то место, где компилятор может использовать вывод типа для объявления анонимного типа.
Другими словами, чтобы пройти Func<Tsource, TResult>
к Select
, вы - вызывающий абонент - должны определить TResult
.Что значит Select
возвращает не анонимный тип, определенный им, а вами.
Чтобы эмулировать это, вам просто нужно заставить вызывающую сторону определить тип:
TResult ReturnAnonymousType<TResult>(Func<TResult> f) {
return f();
}
Console.WriteLine(ReturnAnonymousType(
() => return new { Text = "Hello World!" } // type defined here, before calling
);
Другие советы
Ну, это обычный вывод типа для аргументов типа универсального метода.Например:
List<string> x = new List<string>();
// The compiler converts this:
x.Select(y => y.Length);
// Into this, using type inference:
Enumerable.Select<string, int>(x, y => y.Length);
То же самое было бы верно, если бы x
были списком какого-либо анонимного типа или если предполагаемый тип возвращаемого значения лямбда-выражения был анонимным типом.Не забывайте, что даже если вы не можете явно указать тип переменной, использующей анонимный тип, это все равно возможно. иметь определенный тип, известный компилятору.
Из комментария:«Итак, как бы я реализовал аналогичный метод?»
Все, что вам здесь нужно, это любой общий метод:
public List<T> Foo<T>(T template) { // doesn't actually use "template"
return new List<T>(); // just an example
}
тогда вы можете иметь:
var list = Foo(new {Bar=1});
Компилятор предоставляет <T>
через вывод общего типа.
Немного дерзко, но вы даже можете сделать это, даже не создавая экземпляр типа anon:
public List<T> Foo<T>(Func<T> func) { // doesn't actually use "func"
return new List<T>(); // just an example
}
var list = Foo(() => new {Bar = 1});
Опять же, предоставляется компилятором через возвращаемое значение лямбды.
Тип возвращаемого значения Select является универсальным и в большинстве ситуаций выводится из лямбда-выражения.
Например:
List<int> list = new List<int<();
var val = list.Select(x => new {value = x, mod = x % 10});
Возвращаемое значение select основано на определенном мной анонимном типе и экстраполируется из лямбда-выражения в делегат и в функцию Select.Функция Select в этом случае не знает и не заботится о конкретном анонимном типе, поскольку с ее точки зрения это универсальный тип.