首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >IEnumerable<T>.Union(IEnumerable<T>)覆盖内容,而不是联合

IEnumerable<T>.Union(IEnumerable<T>)覆盖内容,而不是联合
EN

Stack Overflow用户
提问于 2012-08-13 22:02:55
回答 5查看 1.4K关注 0票数 3

我有一个项目集合(ADO.NET实体框架),需要根据几个不同的条件返回一个子集作为搜索结果。不幸的是,条件重叠的方式使得我不能只获取满足条件的集合(或者丢弃Where Where条件不满足),因为这会遗漏或重复应该返回的有效项。

我决定单独进行每一次检查,并将结果组合在一起。我考虑过使用AddRange,但这会导致结果列表中出现重复(我的理解是每次它都会枚举集合-这里我是对还是错?)。我意识到Union不会插入重复项,并将枚举推迟到必要时(同样,这种理解正确吗?)。

搜索内容如下所示:

代码语言:javascript
运行
复制
IEnumerable<MyClass> Results = Enumerable.Empty<MyClass>();
IEnumerable<MyClass> Potential = db.MyClasses.Where(x => x.Y); //Precondition

int parsed_key;

//For each searchable value
foreach(var selected in SelectedValues1)
{
    IEnumerable<MyClass> matched = Potential.Where(x => x.Value1 == selected);
    Results = Results.Union(matched); //This is where the problem is
}

//Ellipsed....

foreach(var selected in SelectedValuesN) //Happens to be integer
{
    if(!int.TryParse(selected, out parsed_id))
        continue;
    IEnumerable<MyClass> matched = Potential.Where(x => x.ValueN == parsed_id);
    Results = Results.Union(matched); //This is where the problem is
}

然而,看起来Results = Results.Union(matched)的工作方式更像Results = matched。我已经完成了一些测试数据和测试搜索。搜索请求第一个字段为-1、0、1或3的结果。这将返回4个结果(两个0、一个1和一个3)。循环的第一次迭代按预期工作,结果仍然为空。第二次迭代也像预期的那样工作,结果包含两个项目。然而,在第三次迭代之后,结果只包含一项。

我是不是误解了.Union的工作原理,还是有别的原因?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2012-08-13 22:08:14

由于延迟执行,当您最终使用Results时,它是许多Where查询的联合,所有这些查询都基于 selected的最后一个值。

所以你有

代码语言:javascript
运行
复制
Results = Potential.Where(selected)
    .Union(Potential.Where(selected))
    .Union(potential.Where(selected))...

并且所有的selected值都是相同的。

您需要在循环中创建一个var currentSelected = selected并将其传递给查询。这样一来,selected的每个值都将被单独捕获,您就不会遇到这个问题。

票数 8
EN

Stack Overflow用户

发布于 2012-08-13 22:05:51

您可以更简单地做到这一点:

代码语言:javascript
运行
复制
Reuslts = SelectedValues.SelectMany(s => Potential.Where(x => x.Value == s));

(这可能会返回重复项)

代码语言:javascript
运行
复制
Results = Potential.Where(x => SelectedValues.Contains(x.Value));
票数 1
EN

Stack Overflow用户

发布于 2012-08-13 22:39:47

正如其他人所指出的,您的LINQ表达式是一个closure。这意味着变量selected会在foreach循环的每次迭代中被LINQ表达式捕获。在foreach的每次迭代中都使用相同的变量,因此它最终将具有上一个值。为了解决这个问题,您需要在foreach循环中声明一个局部变量,如下所示:

代码语言:javascript
运行
复制
//For each searchable value 
foreach(var selected in SelectedValues1) 
{
    var localSelected = selected;
    Results = Results.Union(Potential.Where(x => x.Value1 == localSelected));
}

只使用.Contains()要短得多

代码语言:javascript
运行
复制
Results = Results.Union(Potential.Where(x => SelectedValues1.Contains(x.Value1)));

因为您需要查询多个SelectedValues集合,所以可以将它们都放在它们自己的集合中并遍历它们,尽管您需要一些方法来匹配对象上的正确字段/属性。

您可以通过将所选值的列表以字段/属性的名称作为键存储在字典中来实现这一点。您将使用反射来查找正确的字段并执行检查。然后,可以将代码缩短为以下代码:

代码语言:javascript
运行
复制
// Store each of your searchable lists here
Dictionary<string, IEnumerable<MyClass>> DictionaryOfSelectedValues = ...;

Type t = typeof(MyType);
// For each list of searchable values
foreach(var selectedValues in DictionaryOfSelectedValues) // Returns KeyValuePair<TKey, TValue>
{
    // Try to get a property for this key
    PropertyInfo prop = t.GetProperty(selectedValues.Key);
    IEnumerable<MyClass> localSelected = selectedValues.Value;

    if( prop != null )
    {
        Results = Results.Union(Potential.Where(x =>
                localSelected.Contains(prop.GetValue(x, null))));
    }
    else // If it's not a property, check if the entry is for a field
    {
        FieldInfo field = t.GetField(selectedValues.Key);
        if( field != null )
        {
            Results = Results.Union(Potential.Where(x =>
                    localSelected.Contains(field.GetValue(x, null))));
        }
    }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11935897

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档