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的延迟执行和闭包咬了。
当你创建一个可枚举对象时,就像你在这里做的那样…
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();