IEnumerable - 更新foreach循环中的对象

时间:2015-12-14 13:39:06

标签: c# foreach ienumerable

我有一个非常简单的程序,可以创建一堆对象并遍历它们来设置每个对象的Priority属性。

static void Main(string[] args)
{
    foreach (var obj in ObjectCreator.CreateObjectsWithPriorities())
        Console.WriteLine(String.Format("Object #{0} has priority {1}",
                                         obj.Id, obj.Priority));
}

class ObjectCreator
{
    public static IEnumerable<ObjectWithPriority> CreateObjectsWithPriorities()
    {
        var objs = new[] { 1, 2, 3 }.Select(i => new ObjectWithPriority() { Id = i });
        ApplyPriorities(objs);
        return objs;
    }

    static void ApplyPriorities(IEnumerable<ObjectWithPriority> objs)
    {
        foreach (var obj in objs)
        {
            obj.Priority = obj.Id * 10;
            Console.WriteLine(String.Format("Set priority of object #{0} to {1}", obj.Id, obj.Priority));
        }
    }
}

class ObjectWithPriority
{
    public int Id { get; set; }
    public int Priority { get; set; }
}

我希望Main方法中的IEnumerable包含具有已修改优先级的对象。但是,它们都具有默认值0。 这是日志:

Set priority of object #1 to 10 
Set priority of object #2 to 20 
Set priority of object #3 to 30 
Object #1 has priority 0 
Object #2 has priority 0 
Object #3 has priority 0

suche行为的原因是什么?为了让我的优先事项有效,我应该在这里做些什么改变?

3 个答案:

答案 0 :(得分:15)

执行此操作时:

var objs = new[] { 1, 2, 3 }.Select(i => new ObjectWithPriority() { Id = i });

您只是创建一个延迟评估的迭代器,这不会分配数组/列表来存储您项目的ObjectWithPriorty。每次枚举迭代器时,它将再次迭代值并为每次迭代投影ObjectWithPriority,但会丢弃它们。

您要做的是在传递查询之前实现查询,因此稍后您将实际修改已分配的列表/数组。这可以使用Enumerable.ToListEnumerable.ToArray

来实现
public static IEnumerable<ObjectWithPriority> CreateObjectsWithPriorities()
{
    var objs = new[] { 1, 2, 3 }.Select(i => new ObjectWithPriority() { Id = i })
                                .ToList();
    ApplyPriorities(objs);
    return objs;
}

您可以额外使用Enumerable.Range而不是分配固定大小的数组,这会根据请求懒散地投射数字:

var objs = Enumerable.Range(1, 3).Select(i => new ObjectWithPriority { Id = i })
                                 .ToList();

答案 1 :(得分:2)

为了更好地了解程序中发生的事情,您应该考虑这个表达式

var objs = new[] { 1, 2, 3 }.Select(i => new ObjectWithPriority() { Id = i });

作为查询,而非作为对象的序列/列表/集合。

但是从你的代码可以明显看出,在这个特定的程序中你不需要查询。您需要一个具有有限数量对象的集合,并且每次使用foreach循环时都会返回相同的对象。

所以要做的事情就是使用ICollection<ObjectWithPriority>代替IEnumerable<ObjectWithPriority>。这样可以更好地代表程序的逻辑,并帮助避免一些错误/误解,就像你偶然发现的那样。

可以按如下方式修改代码:

public static ICollection<ObjectWithPriority> CreateObjectsWithPriorities()
{
    IEnumerable<ObjectWithPriority> queryThatProducesObjectsWithPriorities = new[] { 1, 2, 3 }.Select(i => new ObjectWithPriority() { Id = i }); // just for clarification
    ICollection<ObjectWithPriority> objectsWithPriorities = queryThatProducesObjectsWithPriorities.ToList();
    ApplyPriorities(objectsWithPriorities);
    return objectsWithPriorities;
}

static void ApplyPriorities(ICollection<ObjectWithPriority> objs)
{
    foreach (var obj in objs)
    {
        obj.Priority = obj.Id * 10;
        Console.WriteLine(String.Format("Set priority of object #{0} to {1}", obj.Id, obj.Priority));
    }
}

答案 2 :(得分:0)

除了Yuval Itzchakov的答案:

如果您想延迟加载对象的优先级,可以:

仅针对一个对象定义ApplyPriorities()方法,并在select-Method中使用它或将代理添加到ObjectWithPriority类,并计算优先级,如下面的代码所示:

class ObjectWithPriority
{
    public int Id { get; set; }

    private int? priority;
    public int Priority {
        get
        {
            return (priority.HasValue ? priority.Value : (priority = PriorityProvider(this)).Value);

        }

        set { priority = value; }
    }

    Func<ObjectWithPriority, int> PriorityProvider { get; set; }

    public ObjectWithPriority(Func<ObjectWithPriority, int> priorityProvider = null)
    {
        PriorityProvider = priorityProvider ?? (obj => 10 * obj.Id);
    }
}