LINQ lambda表达式:查找属性为== X的子对象

3

需要在C#中使用LINQ Lambda表达式进行帮助。

让我解释一下我的对象的结构。

RootObject是一个集合(具有多个属性的自定义类),其中有许多属性之一是List<Item> items。 Item包含一个List<Round> Rounds。 Round包含一个EntryRID(此ID是唯一的)和name

string = IDToFind = "111"; //The ID i want a Round object for 

我需要从“items”列表中找到与给定ID(IDToFind)匹配的Round ID。也就是说,我需要在“items”中搜索每个单独的项目,以找到一个与IDToFind匹配的Round对象。

我尝试了以下表达式:

Round playerRound = RootObject.Select(i => i.Items.Select(x => x.Rounds.Where(y => y.EntryRID == Int32.Parse(IDToFind))));

但它并未返回任何对象,而是返回:

System.Linq.Enumerable+WhereSelectListIterator`2[Leaderboards,System.Collections.Generic.IEnumerable`1[System.Collections.Generic.IEnumerable`1[Round]]]

3
是的,你已经创建了一个查询 - 你需要遍历结果...或者使用First()Single()或其他方法来获取单个结果。你可能还想使用SelectMany来展开结果...不过这并没有帮助,因为很难确定你要找什么样的结果类型。 - Jon Skeet
好的,我已经用文字解释过了:),但现在我添加了我要查找的对象(请参见更新后的帖子、表达式)。 - user2408952
如果有多个匹配项,你想要做什么? - Jon Skeet
一个 Round 的 ID 是 100% 独一无二的,因此不可能存在相同的 ID。 - user2408952
@user2408952:但这并不能阻止列表中存在对同一个Round实例的多个引用。基本上,你必须在First、FirstOrDefault等之间做出决定。 - Jon Skeet
显示剩余3条评论
3个回答

3

听起来你想要类似这样的东西:

// Parse the target ID *once* rather than all the time...
var targetId = int.Parse(IDToFind);
var playerRound = RootObject.SelectMany(i => i.Items)
                            .SelectMany(x => x.Rounds)
                            .Where(round => round.EntryRID == targetId)
                            .First();

如果找到第一个匹配的Round,则返回它,否则抛出异常。你可以使用FirstOrDefault,如果没有匹配的对象则返回null,或者使用Single()确保只有一个结果。

作为查询表达式,你可以这样写:

var targetId = int.Parse(IDToFind);
var playerRound = (from foo in RootObject // You haven't told us this type
                   from item in foo.Items
                   from round in item.Rounds
                   where round.EntryRID == targetId
                   select round).First();

谢谢您的建议,我忘记提到RootObject也是一个集合,有许多属性之一是List<Item> items。如果您更新您的答案,我很乐意再试一次。对于我的错误表示抱歉。 - user2408952
@user2408952:我的回答已经处理了这个问题 - 但是你的描述不太准确;集合没有Items属性,而是集合中的每个元素都有该属性。这正是为什么最好使用代码来演示问题而不仅仅是描述的原因... - Jon Skeet
是的,如果我的RootObject属性不是由300行代码构建的话,我本来会发布代码的。因此,我认为没有人会愿意阅读所有300行代码,所以我试图简化它(但似乎我失败了)。 - user2408952
@user2408952:简化是好的 - 但您仍然可以在代码中完成。能够创建一个简短但完整的示例来演示问题是重要的技能 :) - Jon Skeet

2
您的查询返回每个RootObject元素的IEnumerable,每个项的RootObject都会产生一个IEnumerable,并将结果也变成一个IEnumerable。若要将结果展平为单个列表,请使用SelectMany,代码如下:
var matchingItems = RootObject.SelectMany(i => i.Items.SelectMany(x => x.Rounds.Where(y => y.EntryRID == Int32.Parse(IDToFind))));

上面的代码生成了一个集合,您可以进行迭代:
foreach (var item in matchingItems) {
    Console.WriteLine(item.ToString();
}

谢谢您的建议,我忘记提到RootObject也是一个集合,有许多属性之一是List<Item> items。如果您更新您的答案,我很乐意再试一次。对于我的错误表示抱歉。 - user2408952
@user2408952 我原本的假设是RootObject是一个对象集合 - 这就是为什么你可以在它上面执行SelectMany。我忽略了Rounds也需要被展开的事实。 - Sergey Kalinichenko

1
您得到的结果是一个迭代器,即如果您在for循环中使用它,您可以遍历单个元素,例如:
var element = RootObject.Select(i => i.Items.Select(x => x.Rounds.Where(y => y.EntryRID == Int32.Parse(IDToFind))));
for (var element in elements)
{ // do something with the element...}

如果你想立即查看整个列表,也可以使用 ToList() 方法,该方法会获取所有元素并将它们放入一个 List 中。
var list = element.ToList();

谢谢您的建议,我忘了提到RootObject也是一个带有许多属性的集合,其中之一是List<Item> items。如果您更新您的答案,我很乐意再试一次。对我的错误感到抱歉。 - user2408952

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接