Методы и анонимные типы

Я знаю, что вы не можете возвращать анонимные типы из методов, но мне интересно, как метод расширения Select возвращает анонимный тип. Это просто уловка с компилятором?

Редактировать

Предположим, L - это список. Как это работает?

L.Select(s => new { Name = s })

Тип возврата - IEnumerable ‹'a> where' a = new {String Name}


person Rodrick Chapman    schedule 28.11.2008    source источник


Ответы (4)


Тип фактически определяется вызывающим, поэтому он находится в области действия вызывающей функции - аккуратно избегая проблемы «возврата» анонимного типа.

Это достигается путем вывода универсального типа. Подпись для Select: 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 
);
person Mark Brackett    schedule 28.11.2008

Ну, это нормальный вывод типа для аргументов типа универсального метода. Например:

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

person Jon Skeet    schedule 28.11.2008
comment
Собственно, можно ли у вас вообще список анонимных типов? Я думаю, он имел в виду, когда вместо int в вашем примере используется анонимный тип. - person Guvante; 29.11.2008
comment
Да, вы можете легко получить список с элементом анонимного типа: new [] {new {Name = Jon}} .ToList (); - person Jon Skeet; 29.11.2008
comment
Есть и другие способы получения списков - например, путем написания метода расширения и использования одного экземпляра anon (или лямбды для одного), чтобы превратить T в список ‹T› - person Marc Gravell; 29.11.2008

Из комментария: «Итак, как бы я мог реализовать аналогичный метод»

Все, что вам нужно, это любой универсальный метод:

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> через вывод универсального типа.

Немного дерзко, но вы даже можете сделать это, фактически никогда не создавая экземпляр анонимного типа:

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});

Опять же, компилятор предоставляет через возвращаемое значение лямбды.

person Marc Gravell    schedule 28.11.2008

Тип возвращаемого значения Select является универсальным и в большинстве случаев выводится из лямбда-выражения.

Например:

List<int> list = new List<int<();

var val = list.Select(x => new {value = x, mod = x % 10});

Возвращаемое значение select основано на анонимном типе, который я определил, и экстраполируется из лямбда-выражения в делегат в функцию Select. Функция Select в этом случае не знает и не заботится о конкретном анонимном типе, поскольку с ее точки зрения это общий тип.

person Guvante    schedule 28.11.2008
comment
Спасибо, Гуванте. Итак, как мне реализовать аналогичный метод? - person Rodrick Chapman; 29.11.2008