C # cannot set property inside IEnumerable

You see a strange bit of behavior in some C # code that I find it difficult to explain. Maybe I lack an important understanding, so we hope that someone out there can turn on the light for me.

Got a block of code that looks like this:

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; } } } 

When you go through it, everything regarding this code seems to work correctly. The collection of "objects" is populated as expected. "sub" is selected as assembled and has the full set of expected properties, including the filled Title property. No errors occur at runtime.

But ... the SubObject.Title property (which has only the standard get; set; code) that exists in each object remains empty.

I'm at a loss. Does anyone explain what is happening?

EDIT: for those who suggested I don’t use the for and ElementAt loops, I started with the foreach loop, but thought this might be the source of the problem, because every time it built a new SubObject. Fixed now, thanks to your help, and the restoration of ForEach.

Cheers, Matt

+6
source share
5 answers

I would fix it like this:

 var objects = GetObjectsFromApiCall().ToList(); 

Then you can save the loop as it is (it works) or optimize it a bit using foreach and some Linq, as suggested by other answers, but it doesn’t really matter: the problem was that you tried to change the element to IEnumerator <> as described in this question by @Ahmet Kakıcı.

+4
source

try it

 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; } 
+2
source

First of all, you should not use ElementAt() for this type of code, use

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

It should also be noted that if your method returns a dynamic IEnumerable , then every time you call objects.Something() , the API is called again and a new copy is retrieved. If so, you should copy the enumerated list to the list using the .ToList() method.

There is also a way to not put a copy on the list - by creating a dynamic enumerator like this:

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

As for the value that was not set correctly (if the previous things did not help) - try adding throw new Exception(value) to the setter property for the Title property - see if this is the correct value.

+1
source

I invite the GetObjectsFromApiCall function as follows:

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

If I am right, every time you call objects object.ElementAt (i) to get the object, you will get a new object using " yield return new myObject() ".

+1
source

But how can you check if the Title property has changed? Are you calling GetObjectsFromApiCall() again? Or are you foreach again through the same instance of objects ?

An IEnumerable instance can create and create new objects each time they are "listed." So, here is a simple example to illustrate. In this example, define:

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

Then we look at two types of "source", first an array, and then an iterator block, defined as follows:

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

Then check it as follows:

  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 } 

Conclusion: it is quite possible to change the state of a mutable object that belongs to the source that we iterate. But some types of IEnumerable sources give new copies of the data every time they are called, and then it is useless to make changes to the copy.

+1
source

All Articles