【问题标题】:LINQ results change at end of for loopLINQ 结果在 for 循环结束时发生变化
【发布时间】:2014-03-20 18:30:46
【问题描述】:

当对数据源执行一组 LINQ 查询时(我使用的是 LINQ-to-SQL,但这里也发生了仅使用 List<string> 对象),我最终得到不同的结果我的支票。

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

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 循环完成后仍可以访问的准确结果?

【问题讨论】:

  • 当循环退出时,i 将等于 0,这使得所有值都匹配(它们都以空字符串开头)由于 result 被延迟评估,这就是 results.Count() 将循环退出后计数。

标签: c# linq


【解决方案1】:

你被 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();
    }
}

【讨论】:

  • 是的,但我认为@Sam 想要合并所有结果。所以你需要写correctResult = correctResult.Union(queryResult)。此外,queryResult 应该在循环中声明。在循环外 i 被认为具有值 0,因此 queryResult 被认为是:from value in values where value.StartsWith("") select value
  • @nlips,我忘了把它放在我的问题中,但如果找到匹配项,我实际上会中断循环。感谢您指出联合选项!
【解决方案2】:

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

为了避免在 for 循环结束时捕获 i 导致的问题,然后变成 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());  
}

【讨论】:

    【解决方案3】:

    我认为在分配给这样的结果变量时需要调用ToList

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-30
      • 1970-01-01
      • 2013-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多