Is modifying a value type from within a using statement undefined behavior?

后端 未结 4 985
予麋鹿
予麋鹿 2021-02-08 07:08

This one\'s really an offshoot of this question, but I think it deserves its own answer.

According to section 15.13 of the ECMA-334 (on the using statement,

4条回答
  •  情书的邮戳
    2021-02-08 07:55

    This behavior is undefined. In The C# Programming language at the end of the C# 4.0 spec section 7.6.4 (Member Access) Peter Sestoft states:

    The two bulleted points stating "if the field is readonly...then the result is a value" have a slightly surprising effect when the field has a struct type, and that struct type has a mutable field (not a recommended combination--see other annotations on this point).

    He provides an example. I created my own example which displays more detail below.

    Then, he goes on to say:

    Somewhat strangely, if instead s were a local variable of struct type declared in a using statement, which also has the effect of making s immutable, then s.SetX() updates s.x as expected.

    Here we see one of the authors acknowledge that this behavior is inconsistent. Per section 7.6.4, readonly fields are treated as values and do not change (copies change). Because section 8.13 tells us using statements treat resources as read-only:

    the resource variable is read-only in the embedded statement,

    resources in using statements should behave like readonly fields. Per the rules of 7.6.4 we should be dealing with a value not a variable. But surprisingly, the original value of the resource does change as demonstrated in this example:

        //Sections relate to C# 4.0 spec
        class Test
        {
            readonly S readonlyS = new S();
    
            static void Main()
            {
                Test test = new Test();
                test.readonlyS.SetX();//valid we are incrementing the value of a copy of readonlyS.  This is per the rules defined in 7.6.4
                Console.WriteLine(test.readonlyS.x);//outputs 0 because readonlyS is a value not a variable
                //test.readonlyS.x = 0;//invalid
    
                using (S s = new S())
                {
                    s.SetX();//valid, changes the original value.  
                    Console.WriteLine(s.x);//Surprisingly...outputs 2.  Although S is supposed to be a readonly field...the behavior diverges.
                    //s.x = 0;//invalid
                }
            }
    
        }
    
        struct S : IDisposable
        {
            public int x;
    
            public void SetX()
            {
                x = 2;
            }
    
            public void Dispose()
            {
    
            }
        }    
    

    The situation is bizarre. Bottom line, avoid creating readonly mutable fields.

提交回复
热议问题