Understanding Struct, 理解Struct

在.Net 中各种基本类型都是由struct定义,他与引用类型相有一些不同,有自己的特色,这篇BLOG由ECMA335中关于Value Type的定义开始

用Class定义的类型不一定都是引用类型,实际上,值类型就不是(例如:ValueType).用Class定义的ValueType包含了拆箱的值类型和关联的装箱类型. (ECMA335 Partition I, 8.9.7)

ValueType Has A Corresponding Reference Type 

  1. 当调用ValueType上的非静态方法时, this 指针是指向一个托管实例的引用,他会调用关联的装箱类型的方法.
  2. 值类型本身不支持接口,但是他关联的装箱的类型会.
  3. 值类型不能继承
  4. 装箱类型的基类不应该包含任何字段.
  5. 与引用类型不一样,值类型在创建实例时不需要调用构造函数. 默认他会把所有的字段根据类型设置为null或者0.

关于Struct类型使用的一般规则:

1. 不能定义无参数默认构造函数

   1: public struct Employee
   2:     {
   3:         public string Name;
   4:         
   5:         public Employee(string name)
   6:         {
   7:             this.Name = name;
   8:         }
   9:         
  10:         public Employee()
  11:         {
  12:         }
  13:         
  14:         public void Run()
  15:         {
  16:             Console.WriteLine("HI, I am {0}", Name);
  17:         }
  18:     }

错误信息: Structs cannot contain explicit parameterless constructors (Structs 不能包含显示的无参构造)

2. 不能初始化

   1: public struct Employee
   2:     {
   3:         public string Name = "Hello";
   4:     }

错误信息: 'Roger.Testing.Employee.Name': cannot have instance field initializers in structs (Structs中不能包含有初始化后的字段)

3. Read-Only状态不能更改

   1: static readonly Employee emp = new Employee("Roger");
   2:         
   3:         public static void Main()
   4:         {
   5:             emp.Name = "New Roger";
   6:             
   7:             Console.Read();
   8:         }

错误信息: Fields of static readonly field 'Roger.Testing.Testing.emp' cannot be assigned to (except in a static constructor or a variable initializer)  readonly类型的Struct不能被赋值, 除非在静态构造函数或者变量初始化时可以修改

例子:

   1: static readonly Employee emp = new Employee("Roger");
   2:         
   3:         public static void Main()
   4:         {            
   5:             
   6:             Console.Read();
   7:         }
   8:         
   9:         static Testing()
  10:         {
  11:             emp.Name = "New Roger";
  12:         }

 

4. 不能继承

   1: public struct Manager : Employee
   2:     {
   3:         public string Title;
   4:     }

错误信息: 类型'Employee' 在接口列表中但不是接口

5. Struct可以继承无数接口

   1: public struct Manager : IComparable, ICloneable
   2:         {
   3:  
   4:             #region IComparable Members
   5:  
   6:             public int CompareTo(object obj)
   7:             {
   8:                 throw new NotImplementedException();
   9:             }
  10:  
  11:             #endregion
  12:  
  13:             #region ICloneable Members
  14:  
  15:             public object Clone()
  16:             {
  17:                 throw new NotImplementedException();
  18:             }
  19:  
  20:             #endregion
  21:         }

6. StructLayout

详细解释: http://www.cnblogs.com/happyhippy/articles/717028.html

7. 序列化Struct

   1: public static byte[]
   2:     RawSerialize(object anything)
   3: {
   4:     int rawsize =
   5:         Marshal.SizeOf(anything);
   6:     IntPtr buffer =
   7:         Marshal.AllocHGlobal(rawsize);
   8:     Marshal.StructureToPtr(anything,
   9:         buffer, false);
  10:     byte[] rawdata = new byte[rawsize];
  11:     Marshal.Copy(buffer, rawdata,
  12:         0, rawsize);
  13:     Marshal.FreeHGlobal(buffer);
  14:     return rawdata;
  15: }

8. 调用API

详细解释: http://www.yesky.com/165/1621165.shtml

Struct类型上的方法调用

   1: using System;
   2: using System.Collections.Generic;
   3:  
   4: namespace Roger.Testing
   5: {
   6:     public class Testing
   7:     {    
   8:         public static void Main()
   9:         {
  10:             Employee e = new Employee("www.xwang.org");
  11:             e.Run();
  12:             
  13:             Console.Read();
  14:         }
  15:     }
  16:     
  17:     public struct Employee
  18:     {
  19:         public string Name;
  20:         
  21:         public Employee(string name)
  22:         {
  23:             this.Name = name;
  24:         }
  25:         
  26:         public void Run()
  27:         {
  28:             Console.WriteLine("HI, I am {0}", Name);
  29:         }
  30:     }
  31: }

编译IL

   1: .method public hidebysig static void  Main() cil managed
   2: {
   3:   .entrypoint
   4:   // Code size       26 (0x1a)
   5:   .maxstack  2
   6:   .locals init (valuetype Roger.Testing.Employee V_0)
   7:   IL_0000:  ldloca.s   V_0
   8:   IL_0002:  ldstr      "www.xwang.org"
   9:   IL_0007:  call       instance void Roger.Testing.Employee::.ctor(string)
  10:   IL_000c:  ldloca.s   V_0
  11:   IL_000e:  call       instance void Roger.Testing.Employee::Run()
  12:   IL_0013:  call       int32 [mscorlib]System.Console::Read()
  13:   IL_0018:  pop
  14:   IL_0019:  ret
  15: } // end of method Testing::Main

大家可以看到

IL_0007:  call       instance void Roger.Testing.Employee::.Run()

实际上就是调用了开头所说的第一条. :).

和朋友讨论,说难道是每一次方法调用就会产生一次装箱而后调用方法吗?
正如开文所言,Struct的this指针会指向在内存中的方法,将Struct作为第一个参数传给方法,而后执行 (自己没有去内存布局里面看,也没有确认了 :))。

int, double等都属于值类型范畴,所以本身调用方式一致

再看看WINDBG 中方法表的组成

WinDBG Run 方法代码

方法表中包含了RUN方法的调用,进一步证实了一个Associated Boxed Type的存在.

posted on 2008-08-20 00:02  xwang  阅读(611)  评论(0编辑  收藏  举报

导航