0

I have a list of entries which is saved into database by following - shortened - code:

List<MyStruct> myStructList = new JavaScriptSerializer().Deserialize<List<MyStruct>>(postdata);

foreach (MyStruct myStruct in myStructList) {
    if(myStruct.id==0) {
        // Not in DB -> insert.
        myStruct.id = (int)db.ExecuteScalar("INSERT INTO table ...");
    } else {
        ...
    }
}
// return all records with id set to auto-increment value.
return myStructList;

I want to return all records with updated id - but myStruct.id is not writeable, because of the foreach. So I replaced the foreach by a for loop:

for(int i=0;i<myStructList.Count;i++)   //foreach (MyStruct myStruct in myStructList), but writeable
{
    MyStruct myStruct = myStructList[i]
    if(myStruct.id==0) {
        // Not in DB -> insert.
        myStruct.id = (int)db.ExecuteScalar("INSERT INTO table ...");
    }
}
return myStructList;

but changing myStruct does not change myStructList.

Third try: Write back into the List.

for(int i=0;i<myStructList.Count;i++)   //foreach (MyStruct myStruct in myStructList), but writeable
{
    MyStruct myStruct = myStructList[i]
    if(myStruct.id==0) {
        // Not in DB -> insert.
        myStructList[i].id = (int)db.ExecuteScalar("INSERT INTO table ...");
    }
}
return myStructList;

which returns the error:

Cannot modify the return value of 'System.Collections.Generic.List<MyApp.MyStruct>.this[int]' because it is not a variable.

So how on earth can I get this done?

Alexander
  • 19,906
  • 19
  • 75
  • 162
  • you have a typo in `MyStruct myStruct = myStructList[i]`. It must end with a semi-colon `;`. – Ivaylo Slavov May 22 '14 at 08:30
  • @SergeyBerezovskiy: Because the iterator variable is read-only, and `myStruct.id` is part of that variable, because `myStruct` is a value type. – Jon Skeet May 22 '14 at 08:31
  • When to use Struct http://stackoverflow.com/questions/521298/when-to-use-struct-in-c – Droa May 22 '14 at 08:56

1 Answers1

6

The problem is precisely because you've got a struct - and a mutable struct at that.

The indexer will return a value - if the compiler actually did let you change that value, it wouldn't modify the value that's already in the list.

Options:

  • Fetch the value, modify it, then put it back in the list:

    MyStruct myStruct = myStructList[i]
    if (myStruct.id==0)
    {
        myStruct.id = (int)db.ExecuteScalar("INSERT INTO table ...");
        // Copy the modified value back into the list
        myStructList[i] = myStruct;
    }
    
  • Change it to be a class instead of a struct, in which case your first approach will be fine.

  • Make the struct immutable, but create a method that returns a new value which is the same as the old one, but with a new ID. Then you'd use:

    MyStruct myStruct = myStructList[i]
    if (myStruct.id==0)
    {
        myStructList[i] = myStruct.WithId((int)db.ExecuteScalar(...));
    }
    

(You could also create a new list of values instead of modifying the existing list, if you wanted.)

In general, mutable structs are a bad idea.

Community
  • 1
  • 1
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194