C#未能在IEnumerable内设置属性

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(

在一些C#代码中看到了一些奇怪的行为,对此我无法解释。可能是我错过了一个重要的理解点,所以希望有人能帮我打开灯

有一段代码如下所示:

    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()
时都会生成数据的新副本,那么您只是在修改副本。在这种情况下,“原始”数据将保持不变。@我想您是指