我有一组对象,并希望修改该集合中对象的属性。如果我有一个对象,我的ChangeStuff
方法工作正常,从方法返回时对象被修改。 (Main的前4行)
然而,在迭代整个集合时,我想我错过了一些东西,因为我的更改值似乎停留在foreach循环的范围内。
我不必传递对象by ref(或使用out参数),因为我没有返回新值。
对于大代码块感到抱歉,但它的简化程度与我能做到的一样简单,并且仍然可以证明我的问题。
class foobar
{
public string string1;
public string string2;
}
static void Main(string[] args)
{
/***** THIS WORKS!! *****/
foobar singleFb = new foobar { string1 = "foo2", string2 = "bar2" };
ChangeStuff(singleFb);
Console.WriteLine(singleFb.string1 + ", " + singleFb.string2);
Console.ReadLine(); //pause to read output
/***** THIS DOESN'T WORK!! *****/
List<foobar> myList = new List<foobar> { new foobar {string1 = "foo1", string2 = "bar1"}, new foobar {string1 = "foo2", string2 = "bar2"}, new foobar {string1 = "something else", string2 = "something else again"} };
IEnumerable<foobar> fbs = myList.Where(x => x.string1.StartsWith("foo"));
ChangeStuff(fbs);
foreach (foobar fb in fbs)
{
Console.WriteLine(fb.string1 + ", " + fb.string2);
}
Console.ReadLine(); //pause to read output
}
static void ChangeStuff(IEnumerable<foobar> fbs)
{
foreach (foobar fb in fbs)
{
ChangeStuff(fb);
}
}
static void ChangeStuff(foobar fb)
{
if (fb.string1.Contains("2"))
fb.string1 = "changed!";
}
}
为了修改集合中的对象,我需要更改哪些内容?
<小时/> 编辑另外,只是注意到我的收藏实际上完全丢失了#34; foo2&#34;什么时候回来......很奇怪。我实际上在我的应用程序中使用了IQueryable,并且没有遇到过这个问题。即。我有所有的对象,它们都没有被正确修改。不确定这里发生了什么......
<小时/> 编辑2:感谢您的回答,现在非常有意义。如果我按如下方式更改我的
ChangeStuff
方法,它会按照我的预期运行:
static void ChangeStuff(foobar fb)
{
if (fb.string2.Contains("2"))
fb.string2 = "changed!";
}
答案 0 :(得分:7)
您看到的结果是由IEnumerable<T>
的延迟加载特性造成的。第二次枚举“fbs”时,属性“string1”已更改,因此它不再与Where
谓词匹配。
如果您立即枚举IEnumerable
并将结果存储在具体列表中(例如,使用ToList()
或ToArray()
,则第二个枚举将显示您期望的结果:< / p>
IEnumerable<foobar> fbs = myList.Where(x => x.string1.StartsWith("foo")).ToList();
以下是原始版本代码中发生的情况:
List<foobar> myList = new List<foobar> { new foobar { string1 = "foo1", string2 = "bar1" }, new foobar { string1 = "foo2", string2 = "bar2" }, new foobar { string1 = "something else", string2 = "something else again" } };
IEnumerable<foobar> fbs = myList.Where(x => x.string1.StartsWith("foo"));
// here the enumeration yields objects #1 and #2
// object #2 has its "string1" property modified to "changed!"
ChangeStuff(fbs);
// here the enumeration is re-evaluated, and now object #2 no longer matches the predicate
// only object #1 ("foo1") is output
foreach (foobar s in fbs)
{
Console.WriteLine(s.string1);
}
答案 1 :(得分:5)
在处理LINQ时要记住的最重要的事情是方法不返回查询结果,它们返回一个表示查询本身的对象。枚举序列时,执行查询并返回项目。
fbs
是一个查询,用于获取第一个字符串以foo
开头的所有项目。它不是前两个项的集合,即使这些项是在您第一次定义它时执行该查询时将返回的项。
第一次执行查询并迭代结果时,您会返回两个项目并尝试更改它们,只有一项实际上由ChangeStuff
更改。然后,再次执行查询 以打印结果,但现在更改的项目不符合查询的条件,因此不会返回。仅返回未更改的第一个项目。如果您迭代列表而不是查询,则会看到更改的项目。