Методы и анонимные типы - PullRequest
4 голосов
/ 29 ноября 2008

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

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

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

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

Тип возвращаемого значения: IEnumerable <'a>, где' a = new {String Name}

Ответы [ 4 ]

8 голосов
/ 29 ноября 2008

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

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

5 голосов
/ 29 ноября 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> через вывод обобщенного типа.

Немного дерзко, но вы даже можете сделать это, даже не создавая экземпляр типа 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});

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

1 голос
/ 29 ноября 2008

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

Например:

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

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

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

...