как ссылаться на элемент из анонимного типа - PullRequest
0 голосов
/ 09 декабря 2011

У меня есть такой код

var results =   (from c in Customers  
                    join o in Orders  
                    on c.Id equals o.CustomerId  
                    join p in Products  
                    on p.Id equals o.ProductId  
                    select new   
                    {  
                        CustomerId = c.Id,     // this is a GUID  
                        OrderId = o.Id,        // this is a GUID    
                        ProductName = p.ProductName,  
                     }).ToList();  

Допустим, я хочу получить список всех идентификаторов клиентов, которые заказывают продукт с именем = foo Моя проблема в том, что, поскольку это анонимный тип, как я могу ссылаться на название продукта в любом запросе Linq, который я хочу выполнить по результатам?

Ответы [ 4 ]

5 голосов
/ 09 декабря 2011
var filteredResults = results.Where(r => r.ProductName == "X");

Вывод типа компилятора позаботится об этом за вас. Полный ответ на ваш вопрос:

var customerIds = results
    .Where(r => r.ProductName == "X")
    .Select(r => r.CustomerId)
    .Distinct()
    .ToList();

или

var customerIds = (from r in results
                  where r.ProductName == "X"
                  select r.CustomerId)
    .Distinct()
    .ToList();

EDIT

Некоторые соображения по выводу типа

Чтобы выбрать длины из последовательности строк с именем list, вы можете вызвать Select, используя классический синтаксис статического метода или как метод расширения:

Enumerable.Select<string, int>(list, s => s.Length)
list.Select<string, int>(s => s.Length)

Благодаря выводу типа вам не нужны аргументы типа:

Enumerable.Select(list, s => s.Length)
list.Select(s => s.Length)

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

Для анонимных типов вы не можете предоставить первый аргумент типа, потому что тип не имеет имени для использования в исходном коде (вот что) «аноним» означает, в конце концов: «без имени»). (Таким образом, вы можете видеть, что анонимные типы и вывод типов были как критически важными, так и тесно связанными предпосылками для реализации linq в первую очередь.)

Если вы посмотрите IL для приведенного выше примера анонимного типа, вы увидите, что компилятор фактически дал типу имя (которое содержит символы, которые недопустимы в идентификаторах C #). Когда вы вызываете Select, компилятор выводит из типа перечисляемого (IEnumerable<CrazilyNamedAnonymousType>), что аргумент первого типа должен быть анонимного типа, и, как и в примере со строкой, он предоставляет это значение от вашего имени.

2 голосов
/ 09 декабря 2011

В продолжение вашего комментария:

Я запутался в области действия анонимных типов

Сначала давайте четко определим «сферу». Область действия типа определяется как область текста программы, в которой тип может указываться его безусловным именем .

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

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

Я не уверен, что будет видимость этого типа в области видимости метода или класса.

Опять же, давайте четко определим наши термины. Область действия объекта может включать в себя объекты, которые определяют пространства объявлений . Эти пространства объявлений могут объявлять сущности с тем же именем, что и исходная сущность. Эти сущности имеют свои собственные области видимости, которые могут быть вложены в область действия исходной сущности.

В этой ситуации более вложенный объект может «скрыть» менее вложенный объект. Сущность, которая не скрыта таким образом, называется "видимой" в определенном текстовом месте.

Анонимный тип не имеет области видимости и, очевидно, его нельзя скрыть по имени, поскольку у него нет имени. Спрашивать, является ли анонимный тип «видимым» или нет, не имеет смысла; «видимость» имеет смысл только для вещей с именами.

Я думаю, что, учитывая, что этот тип нигде не объявлен, как компилятор может определить, о каком типе я говорю?

Компилятор запоминает каждое текстовое расположение, в котором вы используете анонимный тип в программе. Если любые два из этих местоположений относятся к анонимным типам, которые имеют одинаковые имена полей , одинаковые типы полей и поля имеют в том же порядке , тогда эти два местоположения рассматриваются как использование того же анонимного типа .

Компилятор может затем выдавать один тип для каждого из уникальных анонимных типов, которые вы использовали в вашей сборке. Детали того, как это происходит, увлекательны (*). Я предлагаю вам покопаться в вашей сборке с помощью ILDASM, чтобы посмотреть, как мы это сделаем, если вам интересно.

Если вы создадите «один и тот же» анонимный тип - те же имена, типы и в том же порядке - в двух разных сборках , то анонимные типы не будут рассматриваться как того же типа. Анонимные типы не предназначены для использования через границы сборки.


(*) Мне.

2 голосов
/ 09 декабря 2011

В методе, который генерирует этот анонимный типизированный результат, вы можете продолжать ссылаться на результаты, как если бы вы определили конкретный тип.

var customersWithFoo = results.Where(r => r.ProductName == "foo")
                              .Select(r => r.CustomerId);

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

class QueryResult 
{
   /* relevant properties */
}

А затем спроектируйте этот тип в исходном запросе и верните последовательность этого типа из вашего метода.

public IEnumerable<QueryResult> GetResults()
{
     var results = ...
                   select new QueryResult 
                   {
                       // properties
                   };

     return results;
}
0 голосов
/ 09 декабря 2011

Анонимные типы будут отображаться в intellisense, как и «нормальные» типы.Во время компиляции создается конкретный класс, который представляет ваш анонимный тип, поэтому разница во время выполнения очень мала.

Вот запрос из вашего набора результатов:

var fooOrders = (from x in results 
                where x.ProductName == "foo"
                select x.CustomerId);
...