我对C#(来自C ++背景)和LINQ非常陌生。我读过,每次迭代时LINQ查询都会重新执行,并且除非底层数据发生变化,否则它总是会产生相同的输出。所以我决定用下面的代码测试一下。更改数组中的单个元素(#2)按预期工作,但更改基础对象(#4)不会:
static void Main()
{
int[] scores = new int[] { 97, 92, 81, 60 };
/* *** NEW CODE ***
Func<int[]> funcScores = () => scores;
Console.WriteLine(funcScores()[0]); // 97 as expected
scores[0] = 200;
Console.WriteLine(funcScores()[0]); // 200 as expected
scores = new int[] { 1000, 2000, 3000 };
Console.WriteLine(funcScores()[0]); // 1000 as expected
*/
IEnumerable<int> scoreQuery = GetScores(scores);
//IEnumerable<int> scoreQuery = GetScores(ref scores); #8
foreach (int i in scoreQuery)
{
Console.Write(i + " "); // #1 outputs 97, 92, 81 as expected
}
scores[2] = 343; // #2 change underlying data
Console.WriteLine("\n");
foreach (int i in scoreQuery)
{
Console.Write(i + " "); // #3 outputs 97, 92, 343 as expected
}
scores = new int[] { 100, 200, 3, 5, 6 }; // #4 replace underlying object
//scoreQuery = GetScores(scores); #7
Console.WriteLine("\n");
foreach (int i in scoreQuery)
{
Console.Write(i + " "); // #5 outputs as #3 rather than 100, 200
}
Console.WriteLine("\n");
foreach (var item in scores)
{
Console.Write(item + "\t"); // #6 sanity check scores is 100,200,3,5,6
}
}
private static IEnumerable<int> GetScores(int[] scores)
{
Console.WriteLine("non-ref version");
return from score in scores
where score > 80
select score;
}
private static IEnumerable<int> GetScores(ref int[] scores)
{
Console.WriteLine("ref version");
return from score in scores
where score > 80
select score;
}
重新创建查询(#7)会'修复'它,但不是我想要的。所以我认为我需要做的就是通过ref(#8)传递分数,但这没什么区别。
我哪里错了?我认为我对引用与包含它们的变量的理解存在脱节,或者这是否与LINQ查询的具体内容有关?
答案 0 :(得分:0)
致电时
IEnumerable<int> scoreQuery = GetScores(scores);
scoreQuery实际上是对分数的引用。然后在线:
scores = new int[] { 100, 200, 3, 5, 6 };
你创建一个新数组,并替换存储在变量分数中的引用,但scoreQuery仍引用原始数组。
答案 1 :(得分:0)
你的错误不是LINQ,而是一般的OOP:
无论您如何将初始数组传递给GetScores
,该对象始终由方法返回的IEnumerable<int>
引用。稍后更改调用方法中的引用不会更改该其他引用。
答案 2 :(得分:0)
这句话:
我读过,每次迭代时LINQ查询都会重新执行,并且除非基础数据发生变化,否则它总是产生相同的输出。
指的是如果源对象中的数据发生更改,并且您重复查询,则新的迭代将反映源对象中的更改。它不是指用另一个对象更改源对象。
所有LINQ运算符都按值获取其参数。例如Select
:
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector);
这些LINQ方法是查询语法使用的方法,因此将参数ref
传递给您的方法没有任何区别,因为它们将通过值传递给基础LINQ方法。
答案 3 :(得分:0)
当你写这一行时:
scores = new int[] { 100, 200, 3, 5, 6 }; // #4 replace underlying object
您只需将scores
重新引用到另一个 nothing 的数组,该数组与最初传递给GetScores
方法的数组有关。这与LINQ无关,它是关于如何在传递给方法时处理引用类型(以及数组的内容)。
考虑一下:
var myValue = new MyObject { MyProperty = 1 };
DoSomething(myValue);
现在,当您重新分配myValue
时,请执行以下操作:
myVale = new MyObject { MyProperty = 5 };
您希望DoSomething
如何反映这一点?它不知道关于新实例的任何,因为它是用原始实例调用的。在您的代码中也会发生同样的情况:GetScores
将根据传递给它的实例创建一个迭代器。创建新实例不会改变迭代器的工作方式。