如何在循环IEnumerable时更改值

时间:2014-10-19 20:06:05

标签: c#

我一直陷入这个问题并且已经做了大量的搜索/阅读以试图理解这个问题,但我只是不明白。我想知道是否有人可以提供一个如何在循环IEnumerable<>时更新对象值的示例。

示例:

public class MyClass
{
    private IEnumerable<MyData> _MyData = null;

    public MyClass() {}
    public MyClass(string parm1, int parm2) { Load(parm1, parm2); }

    public IEnumerable<MyData> Load(string parm1, int parm2)
    {
        _MyData = _dbContext.MyData.Where(m => m.Parm1 == parm1 && m.Parm2 == parm2);

        foreach(MyData mydata in _MyData)
        {
            if(mydata.Parm2 == 1)
            {
                mydata.Parm1 = "Some value";
            }
        }
        return _MyData;
    }
}

上述代码不起作用,因为不保留对循环内mydata.Parm1所做的任何更改。我读了一篇SO文章建议做出这样的改动:

for (int i = 0; i < _MyData.Count(); i++)
{
    _MyData.Skip(i).FirstOrDefault().Parm1 = "Some Value";
}

这是有效的,但我真的很难看,并且很难相信没有更清洁的方式。对于单个更改,它可能是可以接受的,但我需要进行一些更改。所以我试过这个:

for (int i = 0; i < _MyData.Count(); i++)
{
    MyData md = _MyData.ElementAt(i);
    md.Parm1 = "Some Value";
}

但我不明白如何将修改后的md放回_MyData中以保存更改。

我认为我经历所有这些环节的主要原因是我不明白如何对元素进行更改并保留它们。除非我通过这些环节,否则我所做的任何更改似乎都会恢复到原始值。也许有人可以指出我哪里出错了。我想像这样使用这个类:

IEnumerable<MyData> MyDataResult = new MyClass("Parm1", 4);

使用上面的代码行,我可以追踪Load()函数并看到正在进行所需的更改,但一旦完成MyDataResult包含原始值而不是我看到的更改

显然我的代码是错误的,但我希望我已经足够清楚地说明我想做什么以及我遇到困难。

2 个答案:

答案 0 :(得分:3)

试试这个 -

_MyData = _dbContext.MyData.Where(m => m.Parm1 == parm1 && m.Parm2 == parm2).ToList();

要清楚,基本上你在当前代码中拥有的是一个查询,你在Load方法中枚举,试图更新它的一些属性,但最后,你最终返回查询,而不是查询中的更新值。

Load方法的调用者将最终再次运行查询,仅查看数据库中的直接值。

.ToList方法中将Load添加到查询末尾,具体化查询,然后您在内存中执行其余操作。

修改 以防万一,如果对此查询可能返回的大量数据存在性能问题,那么您也可以考虑这种替代方法 -

public IEnumerable<MyData> Load(string parm1, int parm2)
{
    _MyData = _dbContext.MyData.Where(m => m.Parm1 == parm1 && m.Parm2 == parm2);

    foreach(MyData mydata in _MyData)
    {
        if(mydata.Parm2 == 1)
        {
            mydata.Parm1 = "Some value";
        }

        yield return myData;
    }
}

这将使查询保持延迟,并仍然返回修改后的值,因为Load的调用者会迭代IEnumerable<MyData>返回的Load

答案 1 :(得分:1)

不确定为什么要返回一个永远不会被使用的值。您只需在_MyData方法中设置Load,因为它是类级变量。 将Load更改为不在db上下文中返回值和ToList()查询,然后将_MyData设置为Load中的具体列表对象。

像这样的东西会起作用 - 我简化了你的例子但是相同的原理:

public class MyClass
{
    public IEnumerable<string> _MyData = null;
    public IEnumerable<string> _dbContext = new List<string> {"a", "b", "aa", "bb"};
    public MyClass(){}
    public MyClass(string parm1, int parm2){Load(parm1, parm2);}

    public void Load(string parm1, int parm2)
    {
        var somedata = _dbContext.Where(m => m.Length == 2).ToList();

        var myData = somedata.Select(s => s == parm1 ? "something else" : s).ToList();

        _MyData = myData;
    }
}

使用可以使用

var a = new MyClass("aa",1)._MyData;

和“aa”将替换为“其他”并返回“a”。

你不能使用这样的类:

IEnumerable<MyData> MyDataResult = new MyClass("Parm1", 4);

如果您要求MyClass,您将获得MyClass而不是IEnumerable

如果您对dbContext的调用是针对底层数据源的,那么最好不要在类构造函数中执行此操作,并始终在类构造后显式调用Load。然后,您可以选择避免在可能长时间运行的I / O操作上阻塞线程。