Automatic Properties are not Fields

December 28, 2012

tags: , ,
no comments

Even though that the statement in the title is trivial, the fact that the syntax of automatic properties and fields is almost identical can sometimes make us forget that they are two different things. Take, for example, the following struct:

   1: [StructLayout(LayoutKind.Sequential)]

   2: public struct MyData

   3: {

   4:     public int A;

   5:     public int B;

   6:     public int C;

   7:     public int D;

   8: }

Let’s assume that it is marshaled and passed to an unmanaged function:

   1: MyData myData = new MyData {A = 1, B = 2, C = 3, D = 4};

   2: IntPtr pData = Marshal.AllocHGlobal(Marshal.SizeOf(myData));

   3: Marshal.StructureToPtr(myData, pData, false);

   4: // Pass pData to some unmanaged function using DllImport

The unmanaged function expects a pointer to a struct that contains 4 integers, where the first integer holds the data for A, the second integer holds the data for B, the third for C, and the forth for D. That is why the struct is decorated with the StructLayout attribute with Sequential LayoutKind, which guides the loader to preserve field order as declared.

What will happen if, for some reason, we implement the C field as an automatic property?

   1: [StructLayout(LayoutKind.Sequential)]

   2: public struct MyData

   3: {

   4:     public int A;

   5:     public int B;

   6:     public int C {get; set;}

   7:     public int D;

   8: }

To answer the question, we need to remember that automatic properties are just a syntactic sugar and when encountered by the compiler, a backing field is automatically generated for them, along with the ordinary get and set methods. The automatically generated backing field will usually be emitted after all the regular fields, and therefore will not preserve the original declaration order.

This is the actual memory layout of the original struct:

   1: 0:000>; !do 02ef260c

   2: Name:        AutomaticPropertiesLayoutDemo.MyData

   3: MethodTable: 0101389c

   4: EEClass:     01011408

   5: Size:        24(0x18) bytes

   6: File:        E:\Moshe\AutomaticPropertiesLayoutDemo\bin\Debug\AutomaticPropertiesLayoutDemo.exe

   7: Fields:

   8:       MT    Field   Offset                 Type VT     Attr    Value Name

   9: 726cc770  4000007        4         System.Int32  1 instance        1 A

  10: 726cc770  4000008        8         System.Int32  1 instance        2 B

  11: 726cc770  4000009        c         System.Int32  1 instance        3 C

  12: 726cc770  400000a       10         System.Int32  1 instance        4 D

And this is the memory layout of the struct with C implemented as an automatic property:

   1: 0:000>; !do 02ef26d8  

   2: Name:        AutomaticPropertiesLayoutDemo.MyData

   3: MethodTable: 0101389c

   4: EEClass:     01011408

   5: Size:        24(0x18) bytes

   6: File:        E:\Moshe\AutomaticPropertiesLayoutDemo\bin\Debug\AutomaticPropertiesLayoutDemo.exe

   7: Fields:

   8:       MT    Field   Offset                 Type VT     Attr    Value Name

   9: 726cc770  4000001        4         System.Int32  1 instance        1 A

  10: 726cc770  4000002        8         System.Int32  1 instance        2 B

  11: 726cc770  4000003        c         System.Int32  1 instance        3 D

  12: 726cc770  4000004       10         System.Int32  1 instance        4 <C>k__BackingField

You can see that the compiler has automatically generated a field named <C>k__BackingField, and placed it after the D field, even though it was before it in the struct’s declaration. Given the above memory layout, the struct will not be marshaled as expected. Specifically, the C and D fields will change places, causing the unmanaged function to read invalid data. Solving the problem by changing the StructLayout to Explicit is not possible as it will cause the compiler to fail with the following error message: "Automatically implemented properties cannot be used inside a type marked with StructLayout(LayoutKind.Explicit)".

The scenario that I presented is fairly esoteric, but may cause some hard to find bugs. I strongly recommend not to use automatic properties in classes or structs that are used across boundaries.

Cross-Posted from http://www.programmingtidbits.com/post/2012/12/28/Automatic-Properties-are-not-Fields.aspx

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*