7

在一些我无法解释的 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;
                }
            }
        }

当您逐步完成它时,有关此代码的所有内容似乎都可以正常工作。“对象”集合按预期填充。“sub”是按收集的方式获取的,并具有一整套预期的属性,包括填充的 Title 属性。执行过程中不会抛出任何错误。

但是......每个 Object 中存在的 SubObject.Title 属性(只有标准的 get;set;代码)顽固地保持为空。

我不知所措。任何人解释发生了什么?

编辑:对于那些建议我不应该使用 for 循环和 ElementAt 的人,我从 foreach 循环开始,但认为它可能是问题的根源,因为它每次都在获取一个新的 SubObject。现在已修复,感谢您的帮助,ForEach 已恢复。

干杯,马特

4

5 回答 5

4

我会这样修复它:

var objects = GetObjectsFromApiCall().ToList();

然后您可以保持循环原样(它有效),或者按照其他答案的建议使用 foreach 和一些 Linq 对其进行一些优化,但这并不重要:问题是您试图更改 IEnumerator< > 正如@Ahmet Kakıcı 指出的这个问题所解释的那样。

于 2013-03-08T13:02:33.393 回答
2

试试这个

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;
}
于 2013-03-08T13:06:06.307 回答
1

首先,你不应该使用ElementAt()这种代码,使用

foreach (var o in objects)
{
    if (string.IsNullOrEmpty(o.SubObject.Title))
    {
        o.SubObject.Title = ...;
    }
}

您还应该注意,如果您的方法返回一个动态的IEnumerable,那么每次您调用objects.Something()API 时都会再次调用该 API 并检索一个新副本。如果是这种情况,您应该使用方法将枚举复制到列表中.ToList()

还有一种不将副本放入列表的方法 - 通过创建一个动态枚举器,如下所示:

objects = objects.Select(o =>
{
    if (string.IsNullOrEmpty(o.SubObject.Title))
    {
        o.SubObject.Title = ...;
    }
    return o;
});

至于未正确设置的值(如果以前的事情没有帮助) - 尝试throw new Exception(value)在属性的设置器中添加 a Title- 看看是否使用正确的值调用它。

于 2013-03-08T13:01:07.457 回答
1

我来宾函数 GetObjectsFromApiCall 如下所示:

public IEnumberable<myObject> GetObjectsFromApiCall(){
    for(var i = 0; i < 10; i++)
    {
         yield return new myObject();
    }
}

如果我是对的,每次调用 objects.ElementAt(i) 函数获取对象时,都会通过“ yield return new myObject()”获取一个新对象。

于 2013-03-08T13:28:36.147 回答
1

但是如何检查Title属性是否更改?你再打电话GetObjectsFromApiCall()吗?还是您再次foreach通过相同的objects实例?

IEnumerable每次“枚举”实例时,它都可能创建并产生新对象。所以这里有一个简单的例子来说明。例如,定义:

class SomeObject
{
    public string Title { get; set; }
}

然后我们将考虑两种类型的“源”,首先是一个数组,然后是一个迭代器块,定义如下:

  static IEnumerable<SomeObject> GetSomeSequence()
  {
      yield return new SomeObject { Title = "Alpha", };
      yield return new SomeObject { Title = "Beta", };
      yield return new SomeObject { Title = "Gamma", };
  }

然后以这种方式测试它:

  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

  }

结论:完全可以修改属于我们正在迭代的源的可变对象的状态。但是某些类型的IEnumerable源每次调用时都会产生数据的新副本,然后对副本进行修改是没有用的。

于 2013-03-08T15:00:12.250 回答