Как использовать Yield Внутри запроса Linq

#c# #linq #yield

#c# #linq #yield

Вопрос:

У меня есть следующий метод:

     public string GetDepartmentTitle(string DepartmentAbbreviation) {
        List<TaxonomyItem> Divisions = TaxonomyFromCMS.GetAllItems(DomainDataConstants.DivisionAndDepartment.TAXONOMY_ID);
        List<TaxonomyItem> Departments = new List<TaxonomyItem>();

        Divisions.ForEach(delegate(TaxonomyItem Division) {
            Departments.AddRange(Division.SubTaxonomyItems);
        });

        TaxonomyItem Result = (from d in Departments
                               where d.Name == DepartmentAbbreviation
                               select d).FirstOrDefault();

        return Result == null ? "" : Result.Title;
    }
  

Сначала он считывает все подразделения (которых всего 3), но у этих подразделений есть много отделов под ними в качестве SubTaxonomyItems. В настоящее время я прохожу по каждому из подразделений, извлекаю каждый из отделов и помещаю их в список под названием Departments. Затем я использую Linq для поиска конкретного элемента.

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

 TaxonomyItem Result = (from d in Departments.SubTaxonomyItems
  

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

     public string GetDepartmentTitle(string DepartmentAbbreviation) {
        List<TaxonomyItem> Divisions = TaxonomyFromCMS.GetAllItems(DomainDataConstants.DivisionAndDepartment.TAXONOMY_ID);

        TaxonomyItem Result = (from d in Divisions.ForEach(delegate(TaxonomyItem Division) {
                                 yeild return Divison.SubTaxonomyItems;
                               }) AS Dps
                               where Dps.Name == DepartmentAbbreviation
                               select Dps).FirstOrDefault();

        return Result == null ? "" : Result.Title;
    }
  

Возможно ли это таким образом или каким-то другим способом, которого я не вижу? Можно ли это вообще сделать без метода расширения?

Ответ №1:

Во-первых, вы можете легко решить свою проблему, просто добавив еще одно «from» к запросу:

 var query = from division in divisions
            from department in division.Departments
            where department.Name == whatever
            select department;
  

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

Это дает вам простой синтаксис для сценария «сшить вместе кучу последовательностей». В более общем плане, однако, иногда вы сталкиваетесь с подобной ситуацией:

 var bars = from foo in foos
           some complicated query logic here
           select foo.bar;
var abcs = from bar in bars
           some other query logic here
           select bar.abc;
  

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

 var abcs = from bar in (
               from foo in foos
               some complicated query logic here
               select foo.bar)
           some other query logic here
           select bar.abc;
  

что некрасиво, или вы можете сделать это:

 var abcs = from foo in foos
           some complicated query logic here
           select foo.bar into bar
           some other query logic here
           select bar.abc;
  

Это делает точно то же самое, но читать его приятнее. Этот синтаксис называется «продолжением запроса».

Чтобы ответить на ваш конкретный вопрос: недопустимо указывать «yield return» в анонимном методе или lambda. Это весьма прискорбно, потому что это было бы действительно полезно. Преобразования, которые выполняет компилятор, чтобы заставить работать анонимные функции и блоки итераторов, довольно сложны, и до сих пор мы всегда пытались заставить их полностью работать вместе. (То есть вы можете поместить лямбда-выражение в блок итератора, но вы не можете поместить блок итератора в лямбда-выражение.) Я надеюсь, но не обещаю, что когда-нибудь мы сможем исправить этот код и разрешить лямбды итератора блокировать. (Помните, размышления Эрика о будущих возможностях языка предназначены только для развлекательных целей.)

Ответ №2:

Похоже, вы просто хотите что-то вроде этого.

 public string GetDepartmentTitle(string DepartmentAbbreviation) {
    var items = TaxonomyFromCMS.GetAllItems(DomainDataConstants.DivisionAndDepartment.TAXONOMY_ID); 
    var result = items.SelectMany(item=>item.SubTaxonomyItems).FirstOrDefault(item=>item.Name == DepartmentAbbreviation);
    var text = result !=null  ? result.Title : String.Empty;
    return text;
}
  

Комментарии:

1. Черт возьми, ты меня опередил!

Ответ №3:

Возврат Yield может использоваться только в very select (каламбур!) местоположения, и запрос Linq не является одним из них. К счастью, здесь это не нужно.

 var q = from division in Divisions
        from dps in division.SubTaxonomyItems
        where dps.Name == DepartmentAbbreviation
        select dps.Title;

return q.FirstOrDefault() ?? String.Empty;
  

Ответ №4:

Почему бы просто не сделать:

 var divisions = TaxonomyFromCMS.GetAllItems
                 (DomainDataConstants.DivisionAndDepartment.TAXONOMY_ID);

var titles = from division in divisions
             from deparment in division.SubTaxonomyItems
             where deparment.Name == DepartmentAbbreviation
             select deparment.Title;

return titles.FirstorDefault() ?? "";
  

Ответ №5:

Это linq, который вы ищете?

 var Result = Divisions.SelectMany(d => d.SubTaxonomyItems).Where(subItem => subItem.Name == DepartmentAbbreviation).FirstOrDefault();
  

Комментарии:

1. Каков тип «подпункта» в вашем коде? Похоже, что это последовательность отделов, но вы рассматриваете это как отдел.

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

3. Правильно. «Выбрать» возвращает последовательность последовательностей элементов . Вы предполагаете, что Select возвращает последовательность элементов — что он «выравнивает» последовательность последовательностей, — но это не то, что делает Select; это то, что делает SelectMany. В вашем коде каждая последовательность в последовательности будет передана в «Where», поэтому «SubItem» будет последовательностью элементов, а последовательность элементов не имеет имени. У каждого элемента есть Название.

4. Интересно. Я проверил разницу, и вы совершенно правы. Select возвращает группы элементов, в то время как SelectMany выбирает отдельные элементы из условия. Я запомню это. Спасибо.