C#未能在IEnumerable内设置属性
在一些C#代码中看到了一些奇怪的行为,对此我无法解释。可能是我错过了一个重要的理解点,所以希望有人能帮我打开灯 有一段代码如下所示:C#未能在IEnumerable内设置属性,c#,.net,linq,C#,.net,Linq,在一些C#代码中看到了一些奇怪的行为,对此我无法解释。可能是我错过了一个重要的理解点,所以希望有人能帮我打开灯 有一段代码如下所示: IEnumberable<myObject> objects = GetObjectsFromApiCall(); for (int i = 0; i < objects.Count(); i++) { if (String.IsNullOrEmpty(objects.ElementAt(
IEnumberable<myObject> objects = GetObjectsFromApiCall();
for (int i = 0; i < objects.Count(); i++)
{
if (String.IsNullOrEmpty(objects.ElementAt(i).SubObject.Title))
{
SubObject sub = GetSubObjectFromDatabase((long)objects.ElementAt(i).SubObject.Id);
if (sub != null)
{
objects.ElementAt(i).SubObject.Title = sub.Title;
}
}
}
objects = objects.Select(o =>
{
if (string.IsNullOrEmpty(o.SubObject.Title))
{
o.SubObject.Title = ...;
}
return o;
});
static IEnumerable<SomeObject> GetSomeSequence()
{
yield return new SomeObject { Title = "Alpha", };
yield return new SomeObject { Title = "Beta", };
yield return new SomeObject { Title = "Gamma", };
}
IEnumberable objects=getObjectsFlomaPicall();
对于(int i=0;i
当您逐步了解它时,这段代码的所有内容似乎都正常工作。“对象”集合按预期填充。“sub”是按收集的方式获取的,并具有一整套预期属性,包括填充的标题属性。执行期间不会抛出任何错误
但是。。。存在于每个对象中的SubObject.Title属性(只有标准的get;set;code)顽固地保持为空
我不知所措。有人解释发生了什么事吗
编辑:对于那些建议我不应该使用For循环和ElementAt的人,我从foreach循环开始,但认为这可能是问题的根源,因为它每次都会获取一个新的子对象。由于您的帮助,现在已修复,ForEach已恢复
干杯,
Matt首先,您不应该使用
ElementAt()
对于此类代码,请使用
foreach (var o in objects)
{
if (string.IsNullOrEmpty(o.SubObject.Title))
{
o.SubObject.Title = ...;
}
}
您还应该注意,如果您的方法返回一个动态的IEnumerable
,那么每次调用objects.Something()
时,都会再次调用API并检索新的副本。如果是这种情况,则应使用.ToList()
方法将可枚举项复制到列表中
还有一种不将副本放入列表的方法-通过创建如下动态枚举器:
IEnumberable<myObject> objects = GetObjectsFromApiCall();
for (int i = 0; i < objects.Count(); i++)
{
if (String.IsNullOrEmpty(objects.ElementAt(i).SubObject.Title))
{
SubObject sub = GetSubObjectFromDatabase((long)objects.ElementAt(i).SubObject.Id);
if (sub != null)
{
objects.ElementAt(i).SubObject.Title = sub.Title;
}
}
}
objects = objects.Select(o =>
{
if (string.IsNullOrEmpty(o.SubObject.Title))
{
o.SubObject.Title = ...;
}
return o;
});
static IEnumerable<SomeObject> GetSomeSequence()
{
yield return new SomeObject { Title = "Alpha", };
yield return new SomeObject { Title = "Beta", };
yield return new SomeObject { Title = "Gamma", };
}
对于未正确设置的值(如果以前的事情没有帮助)-尝试在
Title
属性的setter中添加抛出新异常(值)
-查看是否使用正确的值调用该值。我将通过以下方式修复它:
var objects = GetObjectsFromApiCall().ToList();
static void Main()
{
IEnumerable<SomeObject> thingsToModify;
// set source to an array
thingsToModify = new[] { new SomeObject { Title = "Alpha", }, new SomeObject { Title = "Beta", }, new SomeObject { Title = "Gamma", }, };
foreach (var t in thingsToModify)
Console.WriteLine(t.Title);
foreach (var t in thingsToModify)
t.Title = "Changed!";
foreach (var t in thingsToModify)
Console.WriteLine(t.Title); // OK, modified
// set source to something which yields new object each time a new GetEnumerator() call is made
thingsToModify = GetSomeSequence();
foreach (var t in thingsToModify)
Console.WriteLine(t.Title);
foreach (var t in thingsToModify)
t.Title = "Changed!"; // no-one keeps these modified objects
foreach (var t in thingsToModify)
Console.WriteLine(t.Title); // new objects, titles not modified
}
然后,您可以保持循环的原样(它可以工作),或者按照其他答案的建议使用foreach和一些Linq对其进行一些优化,但这并不重要:问题是您试图更改IEnumerator上的一个元素,正如@Ahmet Kakıcı指出的那样。试试这个
List<myObject> objects = GetObjectsFromApiCall().ToList();
foreach(var obj in objects.Where(o => string.IsNullOrEmpty(objects.SubObject.Title)).ToList())
{
var subObject = GetSubObjectFromDatabase(obj.SubObject.Id);
if(subObject == null) continue;
obj.SubObject.Title = subObject.Title;
}
List objects=GetObjectsFromApiCall().ToList();
foreach(objects.Where(o=>string.IsNullOrEmpty(objects.SubObject.Title)).ToList()中的var obj)
{
var subObject=GetSubObjectFromDatabase(obj.subObject.Id);
如果(子对象==null)继续;
obj.SubObject.Title=子对象.Title;
}
I guest函数GetObjectsFromapCall如下所示:
public IEnumberable<myObject> GetObjectsFromApiCall(){
for(var i = 0; i < 10; i++)
{
yield return new myObject();
}
}
public IEnumberable getObjectsFlomaPicall(){
对于(变量i=0;i<10;i++)
{
返回新的myObject();
}
}
如果我是对的,每次调用objects.ElementAt(I)函数获取对象时,您都会通过“
yield return new myObject()
”获取一个新对象,但是如何检查Title
属性是否已更改?是否再次调用getObjectsFromapCall()
?或者您是否再次通过相同的对象实例执行foreach
IEnumerable
实例每次“枚举”时都可能创建并生成新对象。下面是一个简单的例子。例如,定义:
class SomeObject
{
public string Title { get; set; }
}
我们将考虑两种类型的“源”,首先是数组,然后是这样定义的迭代器块:
IEnumberable<myObject> objects = GetObjectsFromApiCall();
for (int i = 0; i < objects.Count(); i++)
{
if (String.IsNullOrEmpty(objects.ElementAt(i).SubObject.Title))
{
SubObject sub = GetSubObjectFromDatabase((long)objects.ElementAt(i).SubObject.Id);
if (sub != null)
{
objects.ElementAt(i).SubObject.Title = sub.Title;
}
}
}
objects = objects.Select(o =>
{
if (string.IsNullOrEmpty(o.SubObject.Title))
{
o.SubObject.Title = ...;
}
return o;
});
static IEnumerable<SomeObject> GetSomeSequence()
{
yield return new SomeObject { Title = "Alpha", };
yield return new SomeObject { Title = "Beta", };
yield return new SomeObject { Title = "Gamma", };
}
静态IEnumerable GetSomeSequence()
{
返回新的SomeObject{Title=“Alpha”,};
返回新的SomeObject{Title=“Beta”,};
返回新的SomeObject{Title=“Gamma”,};
}
然后通过以下方式进行测试:
var objects = GetObjectsFromApiCall().ToList();
static void Main()
{
IEnumerable<SomeObject> thingsToModify;
// set source to an array
thingsToModify = new[] { new SomeObject { Title = "Alpha", }, new SomeObject { Title = "Beta", }, new SomeObject { Title = "Gamma", }, };
foreach (var t in thingsToModify)
Console.WriteLine(t.Title);
foreach (var t in thingsToModify)
t.Title = "Changed!";
foreach (var t in thingsToModify)
Console.WriteLine(t.Title); // OK, modified
// set source to something which yields new object each time a new GetEnumerator() call is made
thingsToModify = GetSomeSequence();
foreach (var t in thingsToModify)
Console.WriteLine(t.Title);
foreach (var t in thingsToModify)
t.Title = "Changed!"; // no-one keeps these modified objects
foreach (var t in thingsToModify)
Console.WriteLine(t.Title); // new objects, titles not modified
}
static void Main()
{
i可数事物可修改;
//将源设置为数组
thingsToModify=new[]{new SomeObject{Title=“Alpha”,},new SomeObject{Title=“Beta”,},new SomeObject{Title=“Gamma”,},};
foreach(thingsToModify中的变量t)
控制台写入线(t.Title);
foreach(thingsToModify中的变量t)
t、 Title=“已更改!”;
foreach(thingsToModify中的变量t)
Console.WriteLine(t.Title);//确定,修改
//将source设置为每次调用新的GetEnumerator()时生成新对象的值
thingsToModify=GetSomeSequence();
foreach(thingsToModify中的变量t)
控制台写入线(t.Title);
foreach(thingsToModify中的变量t)
t、 Title=“Changed!”//没有人保存这些修改过的对象
foreach(thingsToModify中的变量t)
Console.WriteLine(t.Title);//新对象,未修改标题
}
结论:完全可以修改属于我们正在迭代的源的可变对象的状态。但是,某些类型的IEnumerable
源每次调用时都会生成数据的新副本,因此对副本进行修改是无用的。好了:这段代码可能会非常慢,甚至一点都不好笑。你能复制/粘贴你拥有的实际代码,而不是看起来像实际代码的东西吗?它可以工作,但您可能正在比较数据的不同副本。您能否确认myObject
和子对象
属性(或字段)的类型都是类
类型,而不是struct
(或接口
)类型?如果每次调用方法调用getObjectsFromapCall()
时都会生成数据的新副本,那么您只是在修改副本。在这种情况下,“原始”数据将保持不变。@我想您是指