IEnumerable - 更新 foreach 循环内的对象
IEnumerable - Update objects inside foreach loop
我有一个非常简单的程序,它创建了一堆对象并遍历它们以设置每个对象的 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
这种行为的原因是什么?我应该在此处更改什么以使我的优先级正常工作?
当你这样做时:
var objs = new[] { 1, 2, 3 }.Select(i => new ObjectWithPriority() { Id = i });
您只是在创建一个延迟计算的迭代器,这不会分配一个 array/list 来存储您的项目 ObjectWithPriorty
。每次枚举迭代器时,它都会再次迭代这些值并为每次迭代投影一个 ObjectWithPriority
,但会丢弃它们。
您要做的是在传递查询之前具体化查询,因此稍后您将实际修改已经分配的 list/array。这可以使用 Enumerable.ToList
或 Enumerable.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();
除了Yuval Itzchakov的答案:
如果您想延迟加载对象的优先级,您可以:
只为一个对象定义您的 ApplyPriorities()
方法并在 select 方法中使用它,或者向您的 ObjectWithPriority
class 添加一个委托来计算优先级如下代码所示:
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);
}
}
为了更好地理解你的程序中发生了什么,你应该想到这个表达式
var objs = new[] { 1, 2, 3 }.Select(i => new ObjectWithPriority() { Id = i });
作为 查询,而不是作为 sequence/list/collection 对象。
但是从您的代码可以明显看出,在这个特定程序中您不需要查询。您需要一个包含有限数量对象的集合,并且每次使用 foreach
.
循环遍历它时 returns 相同的对象
所以一个体面的做法是使用 ICollection<ObjectWithPriority>
而不是 IEnumerable<ObjectWithPriority>
。这将更好地代表程序的逻辑,并有助于避免某些 mistakes/misinterpretations 像您偶然发现的那样。
代码可以修改如下:
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));
}
}
我有一个非常简单的程序,它创建了一堆对象并遍历它们以设置每个对象的 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
这种行为的原因是什么?我应该在此处更改什么以使我的优先级正常工作?
当你这样做时:
var objs = new[] { 1, 2, 3 }.Select(i => new ObjectWithPriority() { Id = i });
您只是在创建一个延迟计算的迭代器,这不会分配一个 array/list 来存储您的项目 ObjectWithPriorty
。每次枚举迭代器时,它都会再次迭代这些值并为每次迭代投影一个 ObjectWithPriority
,但会丢弃它们。
您要做的是在传递查询之前具体化查询,因此稍后您将实际修改已经分配的 list/array。这可以使用 Enumerable.ToList
或 Enumerable.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();
除了Yuval Itzchakov的答案:
如果您想延迟加载对象的优先级,您可以:
只为一个对象定义您的 ApplyPriorities()
方法并在 select 方法中使用它,或者向您的 ObjectWithPriority
class 添加一个委托来计算优先级如下代码所示:
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);
}
}
为了更好地理解你的程序中发生了什么,你应该想到这个表达式
var objs = new[] { 1, 2, 3 }.Select(i => new ObjectWithPriority() { Id = i });
作为 查询,而不是作为 sequence/list/collection 对象。
但是从您的代码可以明显看出,在这个特定程序中您不需要查询。您需要一个包含有限数量对象的集合,并且每次使用 foreach
.
所以一个体面的做法是使用 ICollection<ObjectWithPriority>
而不是 IEnumerable<ObjectWithPriority>
。这将更好地代表程序的逻辑,并有助于避免某些 mistakes/misinterpretations 像您偶然发现的那样。
代码可以修改如下:
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));
}
}