Are value types immutable by definition?

后端 未结 12 1524
陌清茗
陌清茗 2020-12-04 09:18

I frequently read that structs should be immutable - aren\'t they by definition?

Do you consider int to be immutable?

int i         


        
12条回答
  •  没有蜡笔的小新
    2020-12-04 09:23

    Last year I wrote a blog post regarding the problems you can run into by not making structs immutable.

    The full post can be read here

    This is an example of how things can go horribly wrong:

    //Struct declaration:
    
    struct MyStruct
    {
      public int Value = 0;
    
      public void Update(int i) { Value = i; }
    }
    

    Code sample:

    MyStruct[] list = new MyStruct[5];
    
    for (int i=0;i<5;i++)
      Console.Write(list[i].Value + " ");
    Console.WriteLine();
    
    for (int i=0;i<5;i++)
      list[i].Update(i+1);
    
    for (int i=0;i<5;i++)
      Console.Write(list[i].Value + " ");
    Console.WriteLine();
    

    The output of this code is:

    0 0 0 0 0
    1 2 3 4 5
    

    Now let's do the same, but substitute the array for a generic List<>:

    List list = new List(new MyStruct[5]); 
    
    for (int i=0;i<5;i++)
      Console.Write(list[i].Value + " ");
    Console.WriteLine();
    
    for (int i=0;i<5;i++)
      list[i].Update(i+1);
    
    for (int i=0;i<5;i++)
      Console.Write(list[i].Value + " ");
    Console.WriteLine();
    

    The output is:

    0 0 0 0 0
    0 0 0 0 0
    

    The explanation is very simple. No, it's not boxing/unboxing...

    When accessing elements from an array, the runtime will get the array elements directly, so the Update() method works on the array item itself. This means that the structs itself in the array are updated.

    In the second example, we used a generic List<>. What happens when we access a specific element? Well, the indexer property is called, which is a method. Value types are always copied when returned by a method, so this is exactly what happens: the list's indexer method retrieves the struct from an internal array and returns it to the caller. Because it concerns a value type, a copy will be made, and the Update() method will be called on the copy, which of course has no effect on the list's original items.

    In other words, always make sure your structs are immutable, because you are never sure when a copy will be made. Most of the time it is obvious, but in some cases it can really surprise you...

提交回复
热议问题