хорошо, нашел это, хотя я не совсем понимаю ответ. Вот два выражения, которые дают желаемые результаты, одно с использованием «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» .Select (sub => new {bots = sub.Descendants («bot»)), во втором из двух выражений, форме «Select», производит перечисление анонимных типов, каждый из которых содержит перечислимые именованные «боты» XElements:
System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement,<>f__AnonymousType0`1[System.Collections.Generic.IEnumerable`1[System....
Мы хотим преобразовать каждое внутреннее перечислимое число в пару {fst, snd}. Начнем с того, что отметим, что следующие два выражения дают одинаковые результаты, но не являются семантически идентичными, как показано ниже. Единственное различие между этими двумя выражениями заключается в том, что в первом из них в строке 3 указано «Let», а во втором - в строке 3 «Select». Они похожи на выражения «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")
;
Тип «ботов» во внешнем «Позволить» в первом выражении отличается от типа «ботов» во внешнем «Выборе» во втором выражении. В «Позволить» тип «ботов» (примерно) 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")
;
}