Вопрос

Я знаю, что вы не можете возвращать анонимные типы из методов, но мне интересно, как метод расширения 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 в этом случае не знает и не заботится о конкретном анонимном типе, поскольку с ее точки зрения это универсальный тип.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top