Обратите внимание на следующий пример, состоящий из определения вложенного элемента XElement и пары выражений Linq. Первое выражение, которое работает, как ожидалось, итеративно выбирает первый и последний XElements на нижнем уровне, выбирая tmp, созданный путем получения ботов (для нижних частей), сохраненных в новых экземплярах анонимного типа для повторного использования имени "боты" . " Второе выражение пытается сделать то же самое, просто используя «Let», но оно вообще не работает. Сначала компилятор жалуется на то, что вывод типа не работает, а затем, когда я добавляю явные типы, он переходит в IObservable и теряется еще больше. Я ожидал, что это будет совершенно незамысловато, и был весьма удивлен и сбит с толку неудачей. Буду признателен, если у кого-нибудь будет минутка посмотреть и посоветовать. Вы можете вставить следующее в LinqPad, добавить ссылку на System.Interactive и увидеть неудачную компиляцию.
var root = new XElement("root",
new XElement("sub",
new XElement("bot", new XAttribute("foo", 1)),
new XElement("bot", new XAttribute("foo", 2))),
new XElement("sub",
new XElement("bot", new XAttribute("foo", 3)),
new XElement("bot", new XAttribute("foo", 4))));
root.Dump("root");
root.Descendants("sub")
.Select(sub => new {bots = sub.Descendants("bot")})
.Select(tmp => new{fst = tmp.bots.First(), snd = tmp.bots.Last()})
.Dump("bottoms")
;
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Let(bots => new{fst = bots.First(), snd = bots.Last()})
.Dump("bottoms2")
;
2 ответа
let
- это просто ключевое слово, упрощающее преобразования, такие как tmp => new{fst = tmp.bots.First(), snd = tmp.bots.Last()}
. Вместо использования метода расширения Let
просто используйте метод Select
:
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Select(bots => new{fst = bots.First(), snd = bots.Last()})
.Dump("bottoms2");
Хорошо, нашел, хотя не совсем понимаю ответ. Вот два выражения, которые дают желаемые результаты, одно с использованием «Let», а другое с использованием «Select»:
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Let(bots => bots.Select(bot => new{fst = bot.First(), snd = bot.Last()}))
.Dump("bottoms2")
;
root.Descendants("sub")
.Select(sub => new {bots = sub.Descendants("bot")})
.Select(tmp => new{fst = tmp.bots.First(), snd = tmp.bots.Last()})
.Dump("bottoms")
;
Первый «Select», .Select (sub => sub.Descendants («bot»)), в первом из двух выражений, форма «Let», производит перечислимое количество элементов XElements, или, точнее,
System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement,System.Collections.Generic.IEnumerable`1[System.Xml.Linq.XElement]]
Первое «Выбрать» .Select (sub => new {bots = sub.Descendants («bot»)) во втором из двух выражений, форме «Выбрать», создает перечислимое количество анонимных типов, каждый из которых содержит перечислимое имя «боты» XElements:
System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement,<>f__AnonymousType0`1[System.Collections.Generic.IEnumerable`1[System....
Мы хотим преобразовать каждое из внутренних перечислимых элементов в пару {fst, snd}. Начнем с того, что следующие два выражения дают одинаковые результаты, но не идентичны семантически, как показано ниже. Единственное различие между этими двумя выражениями состоит в том, что первое имеет «Let» в строке 3, а второе - «Select» в строке 3. Они точно такие же, как выражения «answer», за исключением того, что в них нет внутреннего трансформации.
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Let(bots => bots.Select(bot => bot))
.Dump("bottoms3")
;
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Select(bots => bots.Select(bot => bot))
.Dump("bottoms4")
;
Тип «ботов» во внешнем «Let» в первом выражении отличается от типа «ботов» во внешнем «Select» во втором выражении. В «Let» тип «ботов» (примерно) IEnumerable<IEnumerable<XElement>>
(его полное название -
System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement,System.Collections.Generic.IEnumerable`1[System.Xml.Linq.XElement]]
Мы можем увидеть более подробно, выделив внутренности, что каждый «бот» в «ботах» является IEnumerable<XElement>
:
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Let(bots =>
{
bots.GetType().Dump("bots in Let");
return bots.Select(bot => bot.GetType());
})
.Dump("Types of bots inside the LET")
;
Types of bots inside the LET
IEnumerable<Type> (2 items)
typeof (IEnumerable<XElement>)
typeof (IEnumerable<XElement>)
Во внешнем "Select" тип "ботов"
System.Xml.Linq.XContainer+<GetDescendants>d__a
Путем анализа, параллельного приведенному выше, мы видим, что каждый «бот» в «ботах» является IEnumerable чего-то, и что что-то является XElement.
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Let(bots =>
{
bots.GetType().Dump("bots in Let");
return bots.Select(bot => bot.GetType());
})
.Dump("Types of bots inside the LET")
;
Types of bots inside the SELECT
IEnumerable<IEnumerable<Type>> (2 items)
IEnumerable<Type> (2 items)
typeof (XElement)
typeof (XElement)
IEnumerable<Type> (2 items)
typeof (XElement)
typeof (XElement)
Заманчиво думать о них как о семантически одинаковом, но это не так. Существует на один уровень неявной упаковки на уровне типа в форме «Выбрать», чем в форме «Разрешить», или наоборот, в зависимости от вашей точки зрения.
Кроме того, очевидно, что «Let» «запускается» один раз над результатом .Select (sub => sub.Descendants («bot»)), тогда как «Select» запускается несколько раз, по одному разу над каждым результатом. Следующее неверно, потому что он игнорирует этот «уровень упаковки».
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Let(bots => new{fst = bots.First(), snd = bots.Last()})
.Dump("bottoms2")
;
Как я уже сказал, я еще не до конца понимаю все детали этого явления. Возможно, с помощью еще нескольких примеров и еще одной ночи, которую я потерял из-за этого, я начну развивать более тонкую интуицию по этому поводу. Вот мой полный сценарий LinqPad на тот случай, если вы так сильно заинтересованы поиграть с этой тонкостью:
void Main()
{
Console.WriteLine ("Here is a sample data set, as XML:");
var root = new XElement("root",
new XElement("sub",
new XElement("bot", new XAttribute("foo", 1)),
new XElement("bot", new XAttribute("foo", 2))),
new XElement("sub",
new XElement("bot", new XAttribute("foo", 3)),
new XElement("bot", new XAttribute("foo", 4))));
root.Dump("root");
Console.WriteLine ("The following two expressions produce the same results:");
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Let(bots => bots.Select(bot => new{fst = bot.First(), snd = bot.Last()}))
.Dump("LET form: bottoms1")
;
root.Descendants("sub")
.Select(sub => new {bots = sub.Descendants("bot")})
.Select(tmp => new{fst = tmp.bots.First(), snd = tmp.bots.Last()})
.Dump("SELECT form: bottoms2")
;
Console.WriteLine ("Analysis of LET form");
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Dump("Top-Level Select in the \"Let\" form:")
;
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.GetType()
.Dump("Type of the top-Level Select in the \"Let\" form:")
;
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Let(bots => bots.Select(bot => bot))
.Dump("Let(bots => bots.Select(bot => bot))")
;
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Let(bots =>
{
bots.GetType().Dump("bots in Let");
return bots.Select(bot => bot.GetType());
})
.Dump("Types of bots inside the LET")
;
Console.WriteLine ("Analysis of SELECT form");
root.Descendants("sub")
.Select(sub => new {bots = sub.Descendants("bot")})
.Dump("Top-level Select in the \"Select\" form:")
;
root.Descendants("sub")
.Select(sub => new {bots = sub.Descendants("bot")})
.GetType()
.Dump("Type of the top-level Select in the \"Select\" form:")
;
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Select(bots => bots.Select(bot => bot))
.Dump("bots => bots.Select(bot => bot)")
;
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Select(bots =>
{
bots.GetType().Dump("bots in Select");
return bots.Select(bot => bot.GetType());
})
.Dump("Types of bots inside the SELECT")
;
}
Похожие вопросы
Новые вопросы
linq
Language Integrated Query (LINQ) - это компонент Microsoft .NET Framework, который добавляет собственные возможности запроса данных в языки .NET. При необходимости рассмотрите возможность использования более подробных тегов, например [linq-to-sql], [linq-to-entity] / [entity-framework] или [plinq]
let
не является просто синтаксическим сахаром в языке выражений C #? Мне неизвестен какой-либо метод расширенияLet
, который вы бы использовали с синтаксисом метода.