What happens when you change a struct to a class in .NET if you don’t recompile the type’s dependencies?
An interesting issue arose out of a recent code review I was conducting. If you change a type from a struct to a class, do you have to recompile all modules that are dependent on that type?
Let’s use a simple example. The following diagram illustrates several packages that are dependent on a “Geometry” package, with the Geometry package containing a struct called “Point”:
Point is defined as follows:
1: public struct Point
2: {
3: public int X;
4: public int Y;
5: }
In the dependent module, I’ve defined a class called “Consumer” and created a constructor that references the Point struct:
1: public class Consumer
2: {
3: public Consumer()
4: {
5: var point = new Point
6: {
7: X = 1,
8: Y = 5
9: };
10: }
11: }
Let’s examine this constructor in IL:
1: .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
2: {
3: .maxstack 2
4: .locals init (
5: [0] valuetype [DependentModule]Geometry.Point point,
6: [1] valuetype [DependentModule]Geometry.Point <>g__initLocal0,
7: [2] valuetype [DependentModule]DependentModule.Point CS$0$0000)
8: L_0000: ldarg.0
9: L_0001: call instance void [mscorlib]System.Object::.ctor()
10: L_0006: nop
11: L_0007: nop
12: L_0008: ldloca.s CS$0$0000
13: L_000a: initobj [ExampleModule]DependentModule.Point
14: L_0010: ldloc.2
15: L_0011: stloc.1
16: L_0012: ldloca.s <>g__initLocal0
17: L_0014: ldc.i4.1
18: L_0015: stfld int32 [ExampleModule]DependentModule.Point::X
19: L_001a: ldloca.s <>g__initLocal0
20: L_001c: ldc.i4.5
21: L_001d: stfld int32 [ExampleModule]DependentModule.Point::Y
22: L_0022: ldloc.1
23: L_0023: stloc.0
24: L_0024: nop
25: L_0025: ret
26: }
Pay attention to lines 5 through 7; the “valuetype” instruction declares an instance variable as a value type. This is important, because it means that the memory that the variable “point” occupies will be passed by value, not by reference. Also notice line 13; this call’s the Point struct’s constructor.
If the Point struct changes to a class, the IL in lines 5 through 7 will throw an exception, because classes can’t be declared as value types. If we change, the Point struct to a class and recompile the Geometry and dependent modules, the constructor’s init block looks like this:
4: .locals init (
5: [0] class [ExampleModule]DependentModule.Point point,
6: [1] class [ExampleModule]DependentModule.Point <>g__initLocal0)
Notice how the point variable is now being defined with the “class” instruction, telling the native-compiler to treat this variable as a reference type. Also, the call to initialize the variable has changed:
13: L_0008: newobj instance void [DependentModule]Geometry.Point::.ctor()
Instead of using the “initobj” instruction, the “newobj” instruction is being used.
The impact of this nuance is very small, because everybody is either using a continuous-integration server to build their solutions (I hope). However, when you’re attempting to perform local changes on your development box this problem can manifest itself.
This entry was posted on Monday, May 18, 2009 at 8:56 AM and is filed under c#, gotchas. You can follow any responses to this entry through the RSS 2.0. You can leave a response.
# by Nhà Xinh - April 21, 2015 at 10:32 AM
Nếu bạn đang muốn đăng tin bán nhà hay bán đất hoặc bạn muốn ban nha quan 12 , nha dat xinh thì hãy đến với chúng tôi rao vat mien phi, với chất lương hàng đầu chúng tôi sẽ giúp các bạn , đăng tin và xem các khu vực nha dat go vap, nha dat quan 9, nha dat thu duc , nha dat binh tan , nha dat tan phu , nha dat tan binh và các khu vực khác trên NguyenManhKha toàn quốc với uy tín và hiệu quả cao khi bạn đến với chúng tôi.