在.Net 中各种基本类型都是由struct定义,他与引用类型相有一些不同,有自己的特色,这篇BLOG由ECMA335中关于Value Type的定义开始
用Class定义的类型不一定都是引用类型,实际上,值类型就不是(例如:ValueType).用Class定义的ValueType包含了拆箱的值类型和关联的装箱类型. (ECMA335 Partition I, 8.9.7)
- 当调用ValueType上的非静态方法时, this 指针是指向一个托管实例的引用,他会调用关联的装箱类型的方法.
- 值类型本身不支持接口,但是他关联的装箱的类型会.
- 值类型不能继承
- 装箱类型的基类不应该包含任何字段.
- 与引用类型不一样,值类型在创建实例时不需要调用构造函数. 默认他会把所有的字段根据类型设置为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. 可以继承无数接口
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: .entrypoint4: // Code size 26 (0x1a)
5: .maxstack 2 6: .locals init (valuetype Roger.Testing.Employee V_0) 7: IL_0000: ldloca.s V_08: IL_0002: ldstr "www.xwang.org"
9: IL_0007: call instance void Roger.Testing.Employee::.ctor(string)
10: IL_000c: ldloca.s V_011: 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: ret15: } // end of method Testing::Main
大家可以看到
IL_0007: call instance void Roger.Testing.Employee::.Run()
实际上就是调用了开头所说的第一条. :)
和朋友讨论,说难道是每一次方法调用就会产生一次装箱而后调用方法吗?
正如开文所言,Struct的this(当前的struct实例)指针会作为实例方法的第一个参数传递给方法,而后执行方法
再看看WINDBG 中方法表的组成
方法表中包含了RUN方法的调用,进一步证实了一个Associated Boxed Type的存在.
参考:
http://rapidapplicationdevelopment.blogspot.com/2007/01/parameter-passing-in-c.html



订阅我的BLOG(RSS)