修改集合中对象的属性

sǝɯ*_*ɯɐſ 1 c# linq ref

我有一组对象,并希望修改该集合中对象的属性.如果我有一个对象,我的ChangeStuff方法工作正常,从方法返回时修改对象.(Main的前4行)

然而,当迭代整个集合时,我想我错过了一些东西,因为我的更改值似乎停留在foreach循环的范围内.

我不应该通过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!";
    }
}
Run Code Online (Sandbox Code Playgroud)

为了修改集合中的对象,我需要更改什么?


编辑:另外,只是注意到我的收藏实际上完全没有"foo2",当它回来时......很奇怪.我实际上在我的应用程序中使用了IQueryable,并没有遇到过这个问题.即.我有所有的对象,他们只是没有被正确修改.不确定这里发生了什么......


编辑2:谢谢你的答案,现在很有道理.如果我ChangeStuff按如下方式改变我的方法,它就像我期望的那样工作:

    static void ChangeStuff(foobar fb)
    {
        if (fb.string2.Contains("2"))
            fb.string2 = "changed!";
    }
Run Code Online (Sandbox Code Playgroud)

McG*_*gle 7

你看到的结果是由于懒惰的性质IEnumerable<T>.第二次枚举"fbs"时,属性"string1"已更改,因此它不再与Where谓词匹配.

如果您IEnumerable立即枚举并将结果存储在具体列表中(例如,通过使用ToList()ToArray(),则第二个枚举将显示您期望的结果:

IEnumerable<foobar> fbs = myList.Where(x => x.string1.StartsWith("foo")).ToList();
Run Code Online (Sandbox Code Playgroud)

以下是原始版本代码中发生的情况:

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);
}
Run Code Online (Sandbox Code Playgroud)


Ser*_*rvy 5

处理LINQ时要记住的最重要的事情是方法不返回查询结果,它们返回一个表示查询本身的对象.枚举序列时,执行查询并返回项目.

fbs是一个查询,以获取第一个字符串开头的所有项目foo.它不是前两个项的集合,即使这些项是在您第一次定义它时执行该查询时将返回的项.

第一次执行查询并迭代结果时,您会返回两个项目,并尝试更改它们,只有一个实际上被更改ChangeStuff.然后再次执行查询以打印结果,但现在更改的项不符合查询的条件,因此不会返回.仅返回未更改的第一个项目.如果您迭代列表而不是查询,您将看到更改的项目.