Трюки с анонимными типами

Пусть читатели еще немного поломают ломают голову над задачками из предыдущего поста - свои ответы я опубликую еще через неделю. Хотя, должен отметить, читатели просто молодцы и отлично справляются с решением задачек. А я тем временем опубликую оставшуюся часть материала презентации на SECR.

Представим себе ситуацию, что в мы создали некоторый метод в котором хотели бы использовать список анонимных типов. Например, описываемых вот так:

var beatleJohn = new { FirstName = "John", LastName = "Lennon" };

При этом мы хотим сохранить строгую типизацию списка, поэтому логичным видится использование обобщенного класса List<T>. Однако, возникает вопрос, как нам чисто синтаксически создать обобщенный список не зная имени типа - фактически для нас анонимные типы, как и следует из названия, являются безымянными. Тут нужно вспомнить о технике создания обобщенной коллекции по экземпляру типа элемента коллекции.

Использование этого метода может выглядеть так:

var beatles = (new[] { beatleJohn }).ToList();

В результате мы получаем строго типизированный список, тип которого легко узнать воспользовавшись Console.WriteLine(beatles.GetType()): выводит симпатичное имячко System.Collections.Generic.List`1[<>f__AnonymousType0`2[System.String,System.String]], однако это знание нам просто для интереса и на практике никак не пригодится.

С полученным списком мы можем работать следующим образом:

beatles.Add(new { FirstName = "Paul", LastName = "McCartney" });
beatles.Add(new { FirstName = "George", LastName = "Harrison" });
beatles.Add(new { FirstName = "Ringo", LastName = "Starr" });

foreach (var beatle in beatles)
{
Console.WriteLine(beatle.FirstName + " " + beatle.LastName);
}

Хорошо, так мы можем создавать обобщенные коллекции анонимных типов, но что мешает воспользоваться этой техникой для...

Перед тем как читать дальше, я прошу слабонервных и легко восприимчивых к грязным трюкам закрыть глаза.

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

Предположим, что у нас есть следующий метод:

static object GetBeatleName() { return new { First = "John", Last = "Lennon" }; }

Метод возвращает некий объект, что замечательно, однако чтобы этим объектом можно было удобно пользоваться, прибегнем к описанному выше методу и определим вспомогательный обобщенный метод для приведения типов:

static T CastType<T>(object obj, T type) { return (T)obj; }

которым воспользуемся так:

var beatle = CastType(obj, new {First = "", Last = ""});

Как и в предыдущем примере мы используем технику типизации экземпляром объекта. В результате можем дальше отлично работать с полученным объектом:

Console.WriteLine("First = {0}, Last = {1}", beatle.First, beatle.Last);

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

Листинг примера 1

using System;
using System.Linq;

namespace ConsoleApplication1
{
class Program
{
static void Main()
{
var beatlesMember = new { FirstName = "John",
LastName = "Lennon" };

            var beatlesList =
(new[] { beatlesMember }).ToList();

            beatles.Add(new { FirstName = "Paul",
LastName = "McCartney" });
beatles.Add(new { FirstName = "George",
LastName = "Harrison" });
beatles.Add(new { FirstName = "Ringo",
LastName = "Starr" });

            foreach (var beatle in beatles)
{
Console.WriteLine(beatle.FirstName +
" " + beatle.LastName);
}
}
}
}

Листинг примера 2

using System;

namespace ConsoleApplication1
{
class Program
{
static object GetBeatleName() {
return new { First = "John", Last = "Lennon" }; }

static T CastType<T>(object obj, T type) {
return (T)obj; }

        static void Main()
{
object obj = GetBeatleName();
var beatle = CastType(obj, new { First = "",
Last = "" });
Console.WriteLine("First={0}, Last={1}",
beatle.First, beatle.Last);
}
}
}