Can't change struct's members value inside

2019-01-15 14:07发布

问题:

Imagine this struct :

        struct Person
        {
             public string FirstName { get; set; }
             public string LastName { get; set; }
        }

And following code :

        var list = new List<Person>();
        list.Add(new Person { FirstName = "F1", LastName = "L1" });
        list.Add(new Person { FirstName = "F2", LastName = "L2" });
        list.Add(new Person { FirstName = "F3", LastName = "L3" });

        // Can't modify the expression because it's not a variable
        list[1].FirstName = "F22";

When I want to change Property's value it gives me the following error:

Can't modify the expression because it's not a variable

While, when I tried to change it inside an array such as Person[] it worked without any error.Is there any problem with my code when using with generic collections?

回答1:

When you return the struct via the List[] indexer, it returns a copy of the entry. So if you assigned the FirstName there, it would just be thrown away. Hence the compiler error.

Either rewrite your Person to be a reference type class, or do a full reassignment:

Person person = list[1];
person.FirstName = "F22";
list[1] = person;

Generally speaking, mutable structs bring about issues such as these that can cause headaches down the road. Unless you have a really good reason to be using them, you should strongly consider changing your Person type.

Why are mutable structs “evil”?



回答2:

Obviously a part of the question is still unanswered. What is difference between List<Person> and Person[]. In term of getting element by index the List calls indexer (method) which returns copy of value-type instance, in opposite array by index returns not a copy but managed pointer to element at the index (used special IL instruction ldelema).

Of course mutable value-types are evil as mentioned in other answers. Look at the simple example.

var en = new {Ints = new List<int>{1,2,3}.GetEnumerator()};
while(en.Ints.MoveNext())
{
    Console.WriteLine(x.Ints.Current);
}

Surprised?



回答3:

Redo your struct as such:

    struct Person
    {
         private readonly string firstName;
         private readonly string lastName;
         public Person(string firstName, string lastName)
         {
             this.firstName = firstName;
             this.lastName = lastName;
         }
         public string FirstName { get { return this.firstName; } }
         public string LastName { get { return this.lastName; } }
    }

And following code as :

    var list = new List<Person>();
    list.Add(new Person("F1", "L1"));
    list.Add(new Person("F2", "L2"));
    list.Add(new Person("F3", "L3"));

    // Can modify the expression because it's a new instance
    list[1] = new Person("F22", list[1].LastName);

This is due to the copy semantics of struct. Make it immutable and work within those constraints and the problem goes away.