There are many similarities between structs in. NET and class, such as direct new, which can be xx.field directly to the members, so that many programmers are working with them, mixing them together, and confusing the difference. What is the difference between the two?
1. Different types
Let's look at a piece of code first
static void Main (string[] args) {Typedemo (); Console.ReadLine (); }//Reference type (because of ' class ') class Someclassref {public Int32 x;} Value type (because of ' struct ') struct Somestructval {public Int32 x;} static void Typedemo () {someclassref r1 = new Someclassref ();//Allocated in heap Somestru Ctval v1 = new Somestructval (); Allocated on stack r1.x = 5; Pointer dereference v1.x = 5; Changed on Stack Console.WriteLine ("R1=" + r1.x.tostring ()); Displays "5" Console.WriteLine ("v1=" + v1.x.tostring ()); ALSO displays "5"//the left side for figure reflects the situation//after the lines Abov E has executed. Someclassref r2 = R1; Copies reference (pointer) only Somestructval v2 = v1; Allocate on Stacks & copies members R1.x = 8; Changes r1.x and r2.x v1.x = 9; Changes v1.x, not v2.x Console.WriteLine ("R1=" + r1.x.tostring ()); Displays "8" Console.WriteLine ("R2=" + r2.x.tostring ()); Displays "8" Console.WriteLine ("v1=" + v1.x.tostring ()); Displays "9" Console.WriteLine ("V2=" + v2.x.tostring ()); Displays "5"//the right side of figure reflects the situation//after all of the lines a Bove has executed. }
The code executes the following results
From the code, we can see that both R1 and V1 are new, and their field x is assigned a value of 5. So the output is r1=5,v1=5.
The R2 and v2 are then defined, and the R2 assignment is r1,v2 assigned to v1. After that, the field x is assigned a value of 8 and 9 respectively, followed by the output of their respective x. The result is that R1 and R2 have the same x value, which becomes 8 of the subsequent assignment. The x value of V1 and V2 is different, R1 is the first assignment of the 5,R2 is the value of the subsequent assignment of 9.
What is the cause of this discrepancy? Let's start with IL to see the generated EXE.
From IL we can see that Someclassref is inherited from System.Object, is Object, and is a reference type. Somestructval is inherited from System.ValueType, which is a value type.
That is, class is a reference type, and a struct is a value type.
The reference type gets the address (or pointer), so that in the R2=R1 assignment process, in fact, the address of the R1 is assigned to R2, so R1 and R2 point to the same object is actually pointed to. The object is stored in the heap.
The value type is directly present in the thread statck. During the assignment of R2=R1, it is a direct copy of the memory data.
Simply put, the assignment of a reference type always has only one object, one data storage space. Value types are assigned several times, several objects, and a few storage spaces. Can see.
R1, R2, V1, v2 are stored in the THREADSTATCK. R1 and R2 point to object in the managed heap. While V1 and v2 are always in thread statck, their field x is immediately after V1 or v2.
2. Function parameters affect different
Our common basic types, such as Int32, bool, and Byte, are all value types and have the same characteristics. Because the assignment of a value type is a copy of the data, the reference type is a reference copy, so the two are very different when passed as function arguments. A value type is a parameter whose original value is not affected, and its original value is affected by the reference type as a parameter. The test code is as follows
static void Main (string[] args) { TestFunc (); Console.ReadLine (); } static void TestFunc () { Someclassref r = new Someclassref (); r.x = 1; Somestructval v = new Somestructval (); v.x = 1; Reffunc (r); Valfunc (v); Console.WriteLine (r.x); Console.WriteLine (v.x); } static void Reffunc (Someclassref r) { r.x = +; } static void Valfunc (Somestructval v) { v.x = +; }
As you can see from the results, r is class, the reference type, and after passing the Reffunc function, the value of its field x becomes 100. V is a struct, the value type, after Valfunc, the value of its field x is still the original value of 1.
3. Different inheritance
Class can inherit, struct cannot inherit.
As you can see, Somestructvalchild inherits Somestructval, and the compilation fails. The hint says it cannot be derived from a sealed type. In this way, the struct is treated as a sealed type. In fact, all value types are sealed (sealed) types. such as Boolean, Char, Int32, UInt64, single, Double, Decimal. Let's take a look at the original version of the CLR via C # Fourth:
Some people will say, here is the use of class to inherit Somestructval, one is class, one is a struct, the two are different, of course, can not inherit. Let's try the struct.
The same compilation cannot be passed, prompting that Somestructval is not an interface.
Of course, in addition to the inheritance of such characteristics, there are many classes of properties in the struct is also not available. such as virtual methods of classes, overloading, and so on.
Trigger of 4.GC
From the previous diagram, we can see that someclassref is a space in the managed heap to store, and in managed HEADP opens up space, it will inevitably trigger garbage collection (GC). The GC is not triggered in the thread statck.
So if you keep going to new a someclassref and new Somestructval, the time will be a big difference. It can be speculated that the somestructval time will certainly be less than someclassref. Here is the test code
Private Const int Time_max = 100000000; static void Main (string[] args) {teststruct (); TestClass (); Console.ReadLine (); } static void Teststruct () {Stopwatch SW = new Stopwatch (); Sw. Start (); for (int i = 0; i < Time_max; i++) {Somestructval v1 = new Somestructval (); v1.x = i; } SW. Stop (); Console.WriteLine ("Struct Time:" + SW.) Elapsedmilliseconds.tostring ()); } static void TestClass () {Stopwatch SW = new Stopwatch (); Sw. Start (); for (int i = 0; i < Time_max; i++) {someclassref r1 = new Someclassref (); r1.x = i; } SW. Stop (); Console.WriteLine ("Class Time:" + SW.) Elapsedmilliseconds.tostring ()); }
Test results See
You can see that the time consumed by class is more than 3 times times that of a struct. Of course, it's time-consuming and the class needs to refer to the user pointer (Type object PTR) and the Sync Block Index (managed) when it opens up space in the HEADP.
5. Box packing and unpacking
For value types, if you want to convert to object, there is a boxing box, and then when you convert from object to a value type, there is a unboxing operation. In the case of reference types, there is no boxing and unpacking process. This also leads to the difference between the packing and unpacking of the struct and class. Here is the test code
static void Boxandunbox_struct () {ArrayList a = new ArrayList (); Somestructval v; Allocate a somestructval (not in the heap). for (Int32 i = 0; i < ten; i++) {v = new somestructval (); v.x = i; Initialize the members in the value type. A.add (v); Box the value type and add the//reference to the Arraylist. } somestructval v2 = (somestructval) a[0]; Console.WriteLine (v2.x); } static void Boxandunbox_class () {ArrayList a = new ArrayList (); Someclassref R; Allocate Someclassref in the heap. for (Int32 i = 0; i < ten; i++) {r = new someclassref (); R.x = i; Initialize the members in the value type. A.add (R); Add the reference to the Arraylist. } someclassref r2 = (someclassref) a[0]; Console.WriteLine (r2.x); }
After compiling, use IL to view exe
Boxandunbox_class function
. method private Hidebysig static void Boxandunbox_class () CIL managed{//code size (0x4b). Maxstack 2. Locals in It ([0] class [Mscorlib]system.collections.arraylist A, [1] class Classandstruct.program/someclassref R, [2] int32 I, [3] class Classandstruct.program/someclassref R2, [4] bool cs$4$0000) IL_0000:NOP il_0001:newobj instance void [Mscorlib]system.collections.arraylist::.ctor () il_0006:stloc.0 il_0007:ldc.i4.0 Il_0008:stloc.2 il_0009:br.s il_0026 Il_000b:nop il_000c:newobj instance void Classandstruct.program /someclassref::.ctor () Il_0011:stloc.1 il_0012:ldloc.1 il_0013:ldloc.2 il_0014:stfld int32 ClassAndStruct . Program/someclassref::x il_0019:ldloc.0 il_001a:ldloc.1 Il_001b:callvirt instance int32 [Mscorlib]system.collec tions. Arraylist::add (object) il_0020:pop il_0021:nop il_0022:ldloc.2 il_0023:ldc.i4.1 il_0024:add Il_0025:stlo C.2 Il_0026:ldLoc.2 il_0027:ldc.i4.s il_0029:clt il_002b:stloc.s cs$4$0000 il_002d:ldloc.s cs$4$0000 il_002f:b RTRUE.S il_000b il_0031:ldloc.0 il_0032:ldc.i4.0 il_0033:callvirt instance Object [mscorlib]system.collection S.arraylist::get_item (Int32) Il_0038:castclass classandstruct.program/someclassref il_003d:stloc.3 Il_003e:ldloc .3 il_003f:ldfld int32 classandstruct.program/someclassref::x il_0044:call void [mscorlib]system.console: : WriteLine (Int32) Il_0049:nop Il_004a:ret}//End of method Program::boxandunbox_class
As you can see, the IL code does not have a box or unbox operation, only a pop-class operation. It is possible to determine that no packing and unpacking is done.
Look again at the Boxandunbox_struct function
. method private Hidebysig static void Boxandunbox_struct () CIL managed{//code size (0x54). Maxstack 2. Locals I NIT ([0] class [Mscorlib]system.collections.arraylist A, [1] valuetype Classandstruct.program/somestructval V, [2] int32 I, [3] ValueType Classandstruct.program/somestructval v2, [4] bool cs$4$0000) il_0 000:nop il_0001:newobj instance void [Mscorlib]system.collections.arraylist::.ctor () il_0006:stloc.0 IL_0007: ldc.i4.0 il_0008:stloc.2 il_0009:br.s il_002e il_000b:nop il_000c:ldloca.s v il_000e:initobj C Lassandstruct.program/somestructval Il_0014:ldloca.s v Il_0016:ldloc.2 il_0017:stfld int32 ClassAndStruct. Program/somestructval::x il_001c:ldloc.0 il_001d:ldloc.1 Il_001e:box classandstruct.program/somestructval Il_0023:callvirt instance Int32 [Mscorlib]system.collections.arraylist::add (object) Il_0028:pop Il_0029:nop IL _002a:ldloc.2 il_002b: ldc.i4.1 il_002c:add il_002d:stloc.2 il_002e:ldloc.2 il_002f:ldc.i4.s il_0031:clt. s cs$4$0000 il_0035:ldloc.s cs$4$0000 il_0037:brtrue.s il_000b il_0039:ldloc.0 il_003a:ldc.i4.0 IL_00 3b:callvirt instance Object [Mscorlib]system.collections.arraylist::get_item (Int32) IL_0040:unbox.any Classandstru Ct. Program/somestructval il_0045:stloc.3 IL_0046:LDLOCA.S v2 il_0048:ldfld int32 classandstruct.program/somes tructval::x il_004d:call void [Mscorlib]system.console::writeline (Int32) Il_0052:nop Il_0053:ret}//End of Method Program::boxandunbox_struct
You can see the Add method in the ArrayList, there is a box operation, and then pop in.
This is because ArrayList's Add method is for object, and Somestructval is a value type, so it must be boxed into an object, and then add it in.
When boxing, the value type will open up space in the managed heap based on the control in Threadstack, and the data will be copied in (the head also adds type ojbect ptr and sync block Index).
This in virtually increases the consumption of space, so the use of ArrayList and other need to carry out the value type of boxing operations, must be considered. Of course ArrayList can be replaced by list<t>. In the generic (<T>) list, the direct target is the T type, so if the generic t of the list is a value type, there will be no boxed operations, which can be thought of as directly opening up spatial data in the Threadstack.
Because of the boxing, so when taking ArrayList data, it is necessary to remove the box.
It is important to note that the unboxing does not simply refer to the data in the managed heap, but instead copies the data to the thread stack. So the two are not an object anymore.
Here is the test code
static void Unbox () { Somestructval v = new Somestructval (); v.x = 1; Object o = v; Somestructval v2 = (somestructval) o; v2.x = one; Somestructval v3 = (somestructval) o; Console.WriteLine ("V2.x=" +v2.x.tostring ()); Console.WriteLine ("O.x=" +v3.x.tostring ()); }
Results such as
Here's the code for IL
. method private Hidebysig static void Unbox () CIL managed{//code size 104 (0x68). Maxstack 2. Locals init ([0] Val Uetype Classandstruct.program/somestructval V, [1] object o, [2] valuetype Classandstruct.program/some Structval v2, [3] valuetype Classandstruct.program/somestructval v3) Il_0000:nop Il_0001:ldloca.s v Il_ 0003:initobj classandstruct.program/somestructval Il_0009:ldloca.s v il_000b:ldc.i4.1 il_000c:stfld I Nt32 classandstruct.program/somestructval::x il_0011:ldloc.0 Il_0012:box classandstruct.program/somestructval Il_0017:stloc.1 il_0018:ldloc.1 IL_0019:unbox.any classandstruct.program/somestructval il_001e:stloc.2 IL_0 01F:LDLOCA.S v2 Il_0021:ldc.i4.s One il_0023:stfld int32 classandstruct.program/somestructval::x IL_0028: Ldloc.1 IL_0029:unbox.any classandstruct.program/somestructval il_002e:stloc.3 il_002f:ldstr "v2.x=" IL_ 0034:LDLOCA.S v2 IL_0036:ldflda int32 classandstruct.program/somestructval::x Il_003b:call instance string [mscorlib]system.in T32::tostring () Il_0040:call string [Mscorlib]system.string::concat (String, string) Il_0045:call void [Mscorlib]system.console::writeline (string) Il_004a:nop Il_ 004b:ldstr "o.x=" Il_0050:ldloca.s V3 il_0052:ldflda int32 classandstruct.program/somestructval::x IL_ 0057:call instance string [Mscorlib]system.int32::tostring () Il_005c:call string [mscorlib]system.string: : Concat (String, String) Il_0061:call void [mscorlib] System.console::writeline (String) Il_0066:nop Il_0067:ret}//End of method Program::unbox
We can see the boxing and unpacking operations in the Il_0012:box IL_0019:unbox.any IL_0029:unbox.any three places.
When the V is assigned to O, a boxing operation is performed, and the O is converted to V2 when the unboxing operation is performed. However, after assigning a value to v2.x, the value of O is not affected (O is converted to v3 output).
In conclusion, we can see that there is a great difference between struct and class, and this difference is due to the difference between the value type and the reference type.
The following summarizes some of the conditions used by the struct for reference only
1. The type is a base type, and its members are basic types, and there are no reference types. And the type does not inherit other types and is not inherited by other types.
2. The instance occupies a small number of bytes (typically less than or equal to 16 bytes)
3. If the instance has more than 16 bytes, but is not passed as a parameter in the method, or when it is returned from the method.
Reprint please indicate the source
http://blog.csdn.net/xxdddail/article/details/36862275