LINQ结果在for循环结束时改变

本文关键字:结束 改变 循环 for 结果 LINQ | 更新日期: 2023-09-27 17:51:05

当对数据源执行一组LINQ查询时(我使用LINQ-to- sql,但这里也只使用List<string>对象),我最终在检查结束时得到不同的结果。

具体来说,下面的代码试图找出是否一个完全限定的域名(FQDN)存在于主机名列表中(并非所有的主机名都是FQDN或在同一域中,但主机标识符对我来说很重要)。搜索试图查找列表中是否存在"host-6.domain.local"或其任何子组件(即"host-6.domain""host-6"),它们不存在。而在for循环中,我们得到了预期的结果,但是一旦for循环完成,我得到的结果具有列表中所有的内容,对我来说,这听起来像是试图找到与空字符串匹配的元素。

void MyMethod()  
{  
    string fqdn = "host-6.domain.local";  
    string[] splitFqdn = fqdn.Split('.');  
    List<string> values = new List<string>();  
    values.add("host-1");  
    values.add("host-2.domain.local");  
    values.add("host-3.domain.local");  
    values.add("host-4");  
    values.add("host-5.other.local");  
    IEnumerable<string> queryResult = null;  
    for (int i = splitFqdn.Length; i > 0; i--)  
    {  
        result =  
            from value in values  
            where value.StartsWith(  
                string.Join(".", splitFqdn.Take(i)))  
            select value;  
        Console.WriteLine(  
            "Inside for loop, take " + i + ": "  + result.Count());  
    }  
    Console.WriteLine();  
    Console.WriteLine(  
        "Outside for loop: " + result.Count());  
}

为什么会发生这种情况,我如何才能得到准确的结果,我仍然可以访问for循环完成后?

LINQ结果在for循环结束时改变

你被LINQ的延迟执行和闭包咬了。

当你创建一个可枚举对象时,就像你在这里做的那样…

result =  
    from value in values  
    where value.StartsWith(  
        string.Join(".", splitFqdn.Take(i)))  
    select value;  

它不会被求值,直到你强制它被求值…例如当你执行result.count()

然后在循环之外,当你再次求值时,result.count()会用for循环中存在的最后一个i值求值,而这个值并没有给你想要的结果。

尝试通过在你的枚举上执行.ToList()来强制评估,就像这样…以下代码显示了两个值,以便您进行比较。

void MyMethod()  
{  
    string fqdn = "host-6.domain.local";  
    string[] splitFqdn = fqdn.Split('.');  
    List<string> values = new List<string>();  
    values.add("host-1");  
    values.add("host-2.domain.local");  
    values.add("host-3.domain.local");  
    values.add("host-4");  
    values.add("host-5.other.local");  
    IEnumerable<string> queryResult = null;
    List<string> correctResult = null;
    for (int i = splitFqdn.Length; i > 0; i--)  
    {  
        queryResult =  
            from value in values  
            where value.StartsWith(  
                string.Join(".", splitFqdn.Take(i)))  
            select value;
        correctResult = queryResult.ToList();
        Console.WriteLine(  
            "Inside for loop, take " + i + ": "  + queryResult.Count());  
    }  
    Console.WriteLine();  
    Console.WriteLine(  
        "Outside for loop queryResult: " + queryResult.Count());  
    Console.WriteLine(  
        "Outside for loop correctResult: " + correctResult.Count());  
}

编辑:感谢nlips指出我没有完全回答这个问题…很抱歉转换为方法语法,但是转换为查询语法需要更长的时间。

void MyMethod()  
{
    string fqdn = "host-6.domain.local";
    string[] splitFqdn = fqdn.Split('.');
    List<string> values = new List<string>();
    values.Add("host-1");
    values.Add("host-2.domain.local");
    values.Add("host-3.domain.local");
    values.Add("host-4");
    values.Add("host-5.other.local");
    values.Add("host-5.other.local");
    IEnumerable<string> queryResult = null;
    List<string> correctResult = new List<string>();
    for (int i = splitFqdn.Length; i > 0; i--)
    {
        correctResult = correctResult
            .Union(values.Where(
                value => value.StartsWith(string.Join(".", splitFqdn.Take(i)))))
            .ToList();
    }
}

我真的很喜欢Kevin对我的问题的回答,但我不是一个对结果调用.ToList()的巨大粉丝,因为这会导致所有匹配的对象从数据库中被拉出来(消耗更多的内存),而不是执行一个查询,简单地得到匹配对象的计数(这是一个快一点的,不需要内存来存储对象),所以使用他的帖子中的信息,我有这个额外的解决方案,它不需要从数据库中提取所有对象,并且只运行COUNT查询(在SQL意义上)。

为了避免捕获i(然后在for循环结束时变成0)所引起的问题,我简单地设置了一个临时变量来保存我正在搜索的值。

void MyMethod()  
{  
    string fqdn = "host-6.domain.local";  
    string[] splitFqdn = fqdn.Split('.');  
    List<string> values = new List<string>();  
    values.add("host-1");  
    values.add("host-2.domain.local");  
    values.add("host-3.domain.local");  
    values.add("host-4");  
    values.add("host-5.other.local");  
    IEnumerable<string> queryResult = null;  
    for (int i = splitFqdn.Length; i > 0; i--)  
    {  
        //taking the line referencing i out of the 
        //query expression prevents referencing i
        //after it is set to 0 outside the for loop
        string temp = string.Join(".", splitFqdn.Take(i));
        //since temp isn't changed anywhere else, it won't
        //get set to an invalid value after the loop exits
        result =  
            from value in values  
            where value.StartsWith(temp)  
            select value;  
        Console.WriteLine(  
            "Inside for loop, take " + i + ": "  + result.Count());  
    }  
    Console.WriteLine();  
    Console.WriteLine(  
        "Outside for loop: " + result.Count());  
}

我认为在分配结果变量时需要调用ToList:

result =  
            (from value in values  
            where value.StartsWith(  
                string.Join(".", splitFqdn.Take(i)))  
            select value).ToList();