九、C# 合式类型
本章要描述如何最终完善类型声明。
1、重写Ojbect中的成员
重写ToString()
默认情况下,在任何对象上调用 ToString()会返回类的完全限定名称,所以有时候需要重载这个函数,来实现更有意义的功能。
重写GetHashCode()
当想要重写Equals()的时候,就应该重写GetHashCode()。
在将类作为散列表集合的键使用时,最好 也将GetHashCode()重写。
散列码的作用是生成与对象的值对应的一个数字,从而高效地平衡一个散列表。
重写GetHashCode()时,请参照以下实现原则。(必须是指为了增强性能而需要采取的措施,安全性是指为了保障安全性而需要采取的措施)
必须:相等的对象必须有相等的散列码(若:a.Equals(b),则a.GetHashCode()=b.GetHashCode() )。
必须:针对一个特定的对象,在这个对象的生存期内,GetHashCode()始终应该返回相同的值,即使对象的数据发生了改变。
在许多时候,应该缓存方法的返回值,从而确保这一点。
必须:GetHashCode()不应引发任何异常;且必须问题能够成功返回一个值。
性能:散列码应尽可能保持唯一。然而,由于散列码返回的只是一个int值,所以只要一种对象包含的值比一个int能够
容纳得多,那么散列码就肯定存在重复。
性能:可能的散列码值就当在int范围内平均分布。
性能:GetHashCode()的性能应该优化。
性能:两个对象的细微差异应造成散列码值的极大差异。
安全性:攻其者应该难以伪造一个具有特定散列码的对象。攻击的手法是向散列表中填写大量都散列成同一个值的数据。
散列表的实现会变成O(n),而不是O(1),造成可能的DOS(拒绝服务)攻击。
重写Equals()
1、对象同一性和相等的对象值
对象的同一性,是指两个引用 引用的是同一个实例。
Object包含一个名为ReferenceEquals()的静态方法,它能显式地检查这种对象同一性
只有引用类型才可能引用相等,因此提供了对同一性概念的支持。为值类型调用ReferenceEquals()将问题返回false
,因为根据定义,值类型直接包含着它的数据。
即使向ReferenceEquals()的两个传递两只一个值类型参数也是false,因为ReferenceEquals()对值类型
进行了装箱。由于 每个实参都被装到一个不同的箱中,所以它们永远不可能引用相等。
2、实现Equals(),包含对象同一性和相等的对象值,属于自定义的相等。本质上可以任意设置相等算法。
以下是为了判断相等的对象值而写。
为了判断两个对象是否相等(具有相同的标识数据),可以用一个对象的Equals()方法。
在Object中,这个virtual方法的实现是用ReferenceEquals()来评判相等性。
重写Equals()的步骤如下:
步骤一:检查是否为null
步骤二:如果是引用类型,就检查引用是否相等
步骤三:检查数据类型是否相等
步骤四:调用一个指定了具体类型的辅助方法,它能将操作数视为要比较的类型,而不是一个对象。
步骤五:可能要检查散列码是否相等 。如果散列码都不相等,就没必要继续执行一次全面的、逐个
字段的比较。(相等的两个对象不可能散列码不同)
步骤六:如果基类重写了Equals(),就检查base.Equals()。
步骤七:比较每一个标识字段,判断是否相等。
步骤八:重写GetHashCode()
步骤九:重写==和!=运算符
相等性实现的指导原则:
1、Equals()、==运算符和!=运算符应该一起实现。
2、一个类型在Equals()、==和!=实现中应该使用相同的算法。
3、实现Equals()、==和!=时,也应该实现一个类型的GetHashCode()方法。
4、GetHashCode()、Equals()、==和!=永远不能引发异常
5、实现IComparable时,与相等性有关的方法也应该实现。
二、运算符重载
实现任何运算符的过程都称为运算符重载。
以下运算符不可被重载
x.y、f(x)、new、typeof、default、checked、unchecked、delegate、is、as、=和=>。
1、比较运算符
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 6 Angle a = new Angle(12,0,0); 7 Angle b = new Angle(24,0,0); 8 Coordinate c1 = new Coordinate(); 9 Coordinate c2 = new Coordinate(); 10 c1.Latitude = a; 11 c2.Latitude = a; 12 c1.Longitude = b; 13 c2.Longitude = b; 14 Console.WriteLine(c1 == c2); 15 Console.WriteLine(c1 != c2); 16 Console.ReadLine(); 17 18 } 19 } 20 struct Angle 21 { 22 23 public Angle(int hours, int minutes, int seconds) 24 { 25 _Hours = hours; 26 _Minutes = minutes; 27 _Seconds = seconds; 28 } 29 public int Hours 30 { 31 get 32 { 33 return _Hours; 34 } 35 } 36 private int _Hours; 37 public int Minutes 38 { 39 get 40 { 41 return _Minutes; 42 } 43 } 44 private int _Minutes; 45 public int Seconds 46 { 47 get 48 { 49 return _Seconds; 50 } 51 } 52 private int _Seconds; 53 public Angle Move(int hours, int minutes, int seconds) 54 { 55 return new Angle(Hours + hours, Minutes + minutes, Seconds + seconds); 56 } 57 } 58 59 class Coordinate 60 { 61 public Angle Latitude 62 { 63 get 64 { 65 return _Latitude; 66 } 67 set 68 { 69 _Latitude = value; 70 } 71 } 72 private Angle _Latitude; 73 74 public Angle Longitude 75 { 76 get 77 { 78 return _Longitude; 79 } 80 set 81 { 82 _Longitude = value; 83 } 84 } 85 private Angle _Longitude; 86 public static bool operator ==(Coordinate leftHandSide, Coordinate rightHandSide) 87 { 88 if (ReferenceEquals(leftHandSide, null)) 89 { 90 return ReferenceEquals(rightHandSide, null); 91 } 92 return (leftHandSide.Equals(rightHandSide)); 93 } 94 public static bool operator !=(Coordinate leftHandSide, Coordinate rightHandSide) 95 { 96 return !(leftHandSide == rightHandSide); 97 98 } 99 //重写 100 public override bool Equals(object obj) 101 { 102 if (obj == null) 103 { 104 return false; 105 } 106 //检查类型是否相同 107 if (this.GetType() != obj.GetType()) 108 { 109 return false; 110 } 111 return Equals((Coordinate)obj); 112 } 113 //重载 114 public bool Equals(Coordinate obj) 115 { 116 if (ReferenceEquals(obj, null)) 117 { 118 return false; 119 } 120 //如果引用相同,肯定为true 121 if (ReferenceEquals(this, obj)) 122 { 123 return true; 124 } 125 //如果散列码值不相同,肯定不同。散列码本身就是根据值生成的整形值 126 if (this.GetHashCode() != obj.GetHashCode()) 127 { 128 return false; 129 } 130 //检查基类是否有自定义的Equals() 131 //System.Diagnostics.Debug.Assert(base.GetType() != typeof(object)); 132 //if (!base.Equals(obj)) 133 //{ 134 // return false; 135 //} 136 //最后,写上自定义的Equals 计算方法 137 return Longitude.Equals(obj.Longitude) && (Latitude.Equals(obj.Latitude)); 138 } 139 //重写 140 public override int GetHashCode() 141 { 142 int hashCode = Longitude.GetHashCode(); 143 hashCode ^= Latitude.GetHashCode();//使用自定义的计算方法(本处常用异或 ) 144 return hashCode; 145 }
输出:true false
注:因为GetHashCode()返回的并非一定是“独一无二”的值(它只能表明操作数不同),所以不能仅仅依赖它来判断两个对象
是否相等。在检查是否为Null时,不可以用null执行相等性检查。否则,就会递归调用== 或者 Equals(),造成一个只有栈举出才会终止
的死循环。为了避免这个问题,需要调用ReferenceEquals()来检查是否为null。
2、二元运算符 如 + -
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 6 Angle a = new Angle(8, 20, 20); 7 Angle b = new Angle(6, 15, 15); 8 Coordinate c1 = new Coordinate(); 9 Coordinate c2 = new Coordinate(); 10 c1.Latitude = a; 11 c2.Latitude = a; 12 c1.Longitude = b; 13 c2.Longitude = b; 14 Console.WriteLine(c1 == c2); 15 Console.WriteLine(c1 != c2); 16 Console.WriteLine((c1 + c2).ToString()); 17 Console.ReadLine(); 18 19 } 20 } 21 struct Angle 22 { 23 24 public Angle(int hours, int minutes, int seconds) 25 { 26 _Hours = hours; 27 _Minutes = minutes; 28 _Seconds = seconds; 29 } 30 public int Hours 31 { 32 get 33 { 34 return _Hours; 35 } 36 } 37 private int _Hours; 38 public int Minutes 39 { 40 get 41 { 42 return _Minutes; 43 } 44 } 45 private int _Minutes; 46 public int Seconds 47 { 48 get 49 { 50 return _Seconds; 51 } 52 } 53 private int _Seconds; 54 public Angle Move(int hours, int minutes, int seconds) 55 { 56 return new Angle(Hours + hours, Minutes + minutes, Seconds + seconds); 57 } 58 } 59 60 class Coordinate 61 { 62 public Angle Latitude 63 { 64 get 65 { 66 return _Latitude; 67 } 68 set 69 { 70 _Latitude = value; 71 } 72 } 73 private Angle _Latitude; 74 75 public Angle Longitude 76 { 77 get 78 { 79 return _Longitude; 80 } 81 set 82 { 83 _Longitude = value; 84 } 85 } 86 private Angle _Longitude; 87 88 public static bool operator ==(Coordinate leftHandSide, Coordinate rightHandSide) 89 { 90 if (ReferenceEquals(leftHandSide, null)) 91 { 92 return ReferenceEquals(rightHandSide, null); 93 } 94 return (leftHandSide.Equals(rightHandSide)); 95 } 96 public static bool operator !=(Coordinate leftHandSide, Coordinate rightHandSide) 97 { 98 return !(leftHandSide == rightHandSide); 99 100 } 101 public static Coordinate operator +(Coordinate leftHandSide, Coordinate rightHandSide) 102 { 103 Coordinate a = new Coordinate(); 104 a.Latitude = new Angle( 105 leftHandSide.Latitude.Hours + rightHandSide.Latitude.Hours, 106 leftHandSide.Latitude.Minutes + rightHandSide.Latitude.Minutes, 107 leftHandSide.Latitude.Seconds + rightHandSide.Latitude.Seconds 108 ); 109 a.Longitude = new Angle( 110 leftHandSide.Longitude.Hours + rightHandSide.Longitude.Hours, 111 leftHandSide.Longitude.Minutes + rightHandSide.Longitude.Minutes, 112 leftHandSide.Longitude.Seconds + rightHandSide.Longitude.Seconds 113 ); 114 return a; 115 } 116 //重写 117 public override bool Equals(object obj) 118 { 119 if (obj == null) 120 { 121 return false; 122 } 123 //检查类型是否相同 124 if (this.GetType() != obj.GetType()) 125 { 126 return false; 127 } 128 return Equals((Coordinate)obj); 129 } 130 //重载 131 public bool Equals(Coordinate obj) 132 { 133 if (ReferenceEquals(obj, null)) 134 { 135 return false; 136 } 137 //如果引用相同,肯定为true 138 if (ReferenceEquals(this, obj)) 139 { 140 return true; 141 } 142 //如果散列码值不相同,肯定不同。散列码本身就是根据值生成的整形值 143 if (this.GetHashCode() != obj.GetHashCode()) 144 { 145 return false; 146 } 147 //检查基类是否有自定义的Equals() 148 //System.Diagnostics.Debug.Assert(base.GetType() != typeof(object)); 149 //if (!base.Equals(obj)) 150 //{ 151 // return false; 152 //} 153 //最后,写上自定义的Equals 计算方法 154 return Longitude.Equals(obj.Longitude) && (Latitude.Equals(obj.Latitude)); 155 } 156 //重写 157 public override int GetHashCode() 158 { 159 int hashCode = Longitude.GetHashCode(); 160 hashCode ^= Latitude.GetHashCode();//使用自定义的计算方法(本处常用异或 ) 161 return hashCode; 162 } 163 //重写 164 public override string ToString() 165 { 166 string str = ""; 167 str = "Latitude " + ((Latitude.Hours.ToString().Length == 2) ? Latitude.Hours.ToString() : "0" + Latitude.Hours.ToString()) + ":" 168 + ((Latitude.Minutes.ToString().Length == 2) ? Latitude.Minutes.ToString() : "0" + Latitude.Minutes.ToString()) + ":" 169 + ((Latitude.Hours.ToString().Length == 2) ? Latitude.Seconds.ToString() : "0" + Latitude.Seconds.ToString()); 170 str += "\n"; 171 str += "Longitude " + ((Longitude.Hours.ToString().Length == 2) ? Longitude.Hours.ToString() : "0" + Longitude.Hours.ToString()) + ":" 172 + ((Longitude.Minutes.ToString().Length == 2) ? Longitude.Minutes.ToString() : "0" + Longitude.Minutes.ToString()) + ":" 173 + ((Longitude.Hours.ToString().Length == 2) ? Longitude.Seconds.ToString() : "0" + Longitude.Seconds.ToString()); 174 return str; 175 } 176 }
输出:
True
False
Latitude 16:40:40
Longitude 12:30:30
3、赋值运算符与二元运算符的结合
只要重载了二元运算符,就自动重载了赋值运算符与二元运算符的结合(+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=)
定义好一个二元运算符后,C#会自动允许将赋值与运算符结合起来作用。
4、条件逻辑运算符
条件运算符 && ||不可以进行重载,但是可以对 & 和 |进行重载 。为了允许一个类型求值为true或false(比如在一个if语句中),有必要
对true/false一元运算符进行重写。
5、一元运算符
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 6 Angle a = new Angle(8, 20, 20); 7 Angle b = new Angle(6, 15, 15); 8 Coordinate c1 = new Coordinate(); 9 c1.Latitude = -a; 10 c1.Longitude = -b; 11 12 Console.WriteLine(c1 ); 13 14 Console.ReadLine(); 15 16 } 17 } 18 struct Angle 19 { 20 21 public Angle(int hours, int minutes, int seconds) 22 { 23 _Hours = hours; 24 _Minutes = minutes; 25 _Seconds = seconds; 26 } 27 public int Hours 28 { 29 get 30 { 31 return _Hours; 32 } 33 } 34 private int _Hours; 35 public int Minutes 36 { 37 get 38 { 39 return _Minutes; 40 } 41 } 42 private int _Minutes; 43 public int Seconds 44 { 45 get 46 { 47 return _Seconds; 48 } 49 } 50 private int _Seconds; 51 public Angle Move(int hours, int minutes, int seconds) 52 { 53 return new Angle(Hours + hours, Minutes + minutes, Seconds + seconds); 54 } 55 //一元运算符重载 56 public static Angle operator -(Angle a) 57 { 58 Angle temp = new Angle(); 59 temp._Hours = 24-a.Hours; 60 temp._Minutes = 60-a.Minutes; 61 temp._Seconds =60- a.Seconds; 62 return temp; 63 } 64 } 65 66 class Coordinate 67 { 68 public Angle Latitude 69 { 70 get 71 { 72 return _Latitude; 73 } 74 set 75 { 76 _Latitude = value; 77 } 78 } 79 private Angle _Latitude; 80 81 public Angle Longitude 82 { 83 get 84 { 85 return _Longitude; 86 } 87 set 88 { 89 _Longitude = value; 90 } 91 } 92 private Angle _Longitude; 93 94 //二元运算符重载 95 public static bool operator ==(Coordinate leftHandSide, Coordinate rightHandSide) 96 { 97 if (ReferenceEquals(leftHandSide, null)) 98 { 99 return ReferenceEquals(rightHandSide, null); 100 } 101 return (leftHandSide.Equals(rightHandSide)); 102 } 103 public static bool operator !=(Coordinate leftHandSide, Coordinate rightHandSide) 104 { 105 return !(leftHandSide == rightHandSide); 106 107 } 108 public static Coordinate operator +(Coordinate leftHandSide, Coordinate rightHandSide) 109 { 110 Coordinate a = new Coordinate(); 111 a.Latitude = new Angle( 112 leftHandSide.Latitude.Hours + rightHandSide.Latitude.Hours, 113 leftHandSide.Latitude.Minutes + rightHandSide.Latitude.Minutes, 114 leftHandSide.Latitude.Seconds + rightHandSide.Latitude.Seconds 115 ); 116 a.Longitude = new Angle( 117 leftHandSide.Longitude.Hours + rightHandSide.Longitude.Hours, 118 leftHandSide.Longitude.Minutes + rightHandSide.Longitude.Minutes, 119 leftHandSide.Longitude.Seconds + rightHandSide.Longitude.Seconds 120 ); 121 return a; 122 } 123 124 //Object成员方法重写 125 //重写 126 public override bool Equals(object obj) 127 { 128 if (obj == null) 129 { 130 return false; 131 } 132 //检查类型是否相同 133 if (this.GetType() != obj.GetType()) 134 { 135 return false; 136 } 137 return Equals((Coordinate)obj); 138 } 139 140 public override int GetHashCode() 141 { 142 int hashCode = Longitude.GetHashCode(); 143 hashCode ^= Latitude.GetHashCode();//使用自定义的计算方法(本处常用异或 ) 144 return hashCode; 145 } 146 147 public override string ToString() 148 { 149 string str = ""; 150 str = "Latitude " + ((Latitude.Hours.ToString().Length == 2) ? Latitude.Hours.ToString() : "0" + Latitude.Hours.ToString()) + ":" 151 + ((Latitude.Minutes.ToString().Length == 2) ? Latitude.Minutes.ToString() : "0" + Latitude.Minutes.ToString()) + ":" 152 + ((Latitude.Hours.ToString().Length == 2) ? Latitude.Seconds.ToString() : "0" + Latitude.Seconds.ToString()); 153 str += "\n"; 154 str += "Longitude " + ((Longitude.Hours.ToString().Length == 2) ? Longitude.Hours.ToString() : "0" + Longitude.Hours.ToString()) + ":" 155 + ((Longitude.Minutes.ToString().Length == 2) ? Longitude.Minutes.ToString() : "0" + Longitude.Minutes.ToString()) + ":" 156 + ((Longitude.Hours.ToString().Length == 2) ? Longitude.Seconds.ToString() : "0" + Longitude.Seconds.ToString()); 157 return str; 158 } 159 //重载 160 public bool Equals(Coordinate obj) 161 { 162 if (ReferenceEquals(obj, null)) 163 { 164 return false; 165 } 166 //如果引用相同,肯定为true 167 if (ReferenceEquals(this, obj)) 168 { 169 return true; 170 } 171 //如果散列码值不相同,肯定不同。散列码本身就是根据值生成的整形值 172 if (this.GetHashCode() != obj.GetHashCode()) 173 { 174 return false; 175 } 176 //检查基类是否有自定义的Equals() 177 //System.Diagnostics.Debug.Assert(base.GetType() != typeof(object)); 178 //if (!base.Equals(obj)) 179 //{ 180 // return false; 181 //} 182 //最后,写上自定义的Equals 计算方法 183 return Longitude.Equals(obj.Longitude) && (Latitude.Equals(obj.Latitude)); 184 } 185 }
输出:
Latitude 16:40:40
Longitude 18:45:45
1 if (c1) 2 { 3 Console.WriteLine(c1); 4 } 5 public static bool operator false(Coordinate c) 6 { 7 if (c.Latitude.Hours < 12) 8 { 9 10 return false; 11 } 12 else 13 { 14 return true; 15 } 16 } 17 public static bool operator true(Coordinate c) 18 { 19 if (c.Latitude.Hours < 12) 20 { 21 return false; 22 } 23 else 24 { 25 return true; 26 } 27 }
6、转换运算符
//类型转换运算符
1 public static implicit operator double(Coordinate c) 2 { 3 return (double)c.Latitude.Hours; 4 } 5 public static implicit operator Coordinate(double hours) 6 { 7 Coordinate c = new Coordinate(); 8 c.Latitude = new Angle((int)hours, 0, 0); 9 return c; 10 }
implict 隐式 explicit显式
注意:实现一个转换运算符时,无论返回值还是参数,都必须是封闭类型,这是为了提供对封装性的支持。C#不允许在被转换类型的作用域
之外指定转换。
三、引用其他程序集
C#和底层CLI平台不是将所有代码都放到一个二进制文件中,而是允许你将代码分散到多个程序集中。
这样一来,就可以在多个可执行文件中重用程序集。
1、类库
开发者可以将程序的不同部分转移到一系列单独编译的单元中,这些单元称为类库,或者简单称为库。
然后,程序可以引用和依靠类库来提供自己的一部分功能。这样一来,两个程序就可以依靠同一个类库,
从而在两个程序中共享那个类库的功能,并减少所需的编码量。
为了重用一个不同程序集中的代码,需要在运行C#编译器的时候引用程序集。通常,引用的程序集是一个类库。
2、附加的类访问修饰符
在类的作用域之外,唯一可用的访问修饰符只有public和internal
internal 也可以用来修饰成员
protected interanl
3、定义命名空间
任何数据类型都是用它的命名空间与名称的组合形式来标识的。
事实上,CLR对“命名空间"是一无所知的。在CLR中,类型的名称都是完全限定的类型名称(它的命名空间与名称的组合形式来标识)。
命名空间的定义可以嵌套。
4、XML注释
C#自带的XML注释功能。
/// <summary>
///
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
XML一般用来生成API文档。
5、垃圾回收
垃圾回收明显是"运行时"的一个核心功能。它的作用是回收不再被引用的对象所战胜的内存。
垃圾回收器只负责 回收内存,不处理其他资源,比如数据库连接、句柄(文件、窗口等)、网络端口以及硬件设备(比如串口)等。
除此之外,垃圾回收器根据是否存在引用来决定要清除什么。这暗示着,垃圾回收器处理的是引用对象,而且只回收堆上的内存。
除此之外,它还意味着假如维持对一个对象的引用,不会阻止垃圾回收器重用对象使用的内存。
.NET的垃圾回收
很长一段,需要再学习,待笔记
弱引用,弱引用是为创建起来代价较高(开销很大),而且维护开销特别大的对象而设计的。
例如,假如一个很大的对象列表要从一个数据库中加载,并向用户显示。
在这种情况下,加载这个列表的代码是很高的。一旦用户关闭列表,它就应该可以进行垃圾回收。但是,
假如用户多次请求这个列表,那么每一次都需要执行代价高昂的加载动作。
为了解决这个问题,可以使用弱引用。然后,就可以使用代码检查列表是否水尚未削除,就重新引用同一个列表。
1 private WeakReference Data; 2 public FileStream GetData() 3 { 4 FileStream data = (FileStream)Data.Target; 5 if (data != null) 6 { 7 return data; 8 } 9 else 10 { 11 //创建新的文件流 12 return data; 13 } 14 }
6、资源清理
6、1终结器(析构函数)
1 public class TemporaryFileStream 2 { 3 private readonly FileStream _Stream; 4 public FileStream Stream 5 { 6 get { return _Stream; } 7 } 8 private FileInfo _File = new FileInfo(Path.GetTempFileName()); 9 public FileInfo File 10 { 11 get { return _File; } 12 } 13 14 public TemporaryFileStream() 15 { 16 _File = new FileInfo(Path.GetTempFileName()); 17 _Stream = new FileStream(File.FullName, FileMode.OpenOrCreate, FileAccess.ReadWrite); 18 } 19 ~TemporaryFileStream() 20 { 21 Close(); 22 } 23 public void Close() 24 { 25 if (Stream != null) 26 { 27 Stream.Close(); 28 } 29 if (File != null) 30 { 31 File.Delete(); 32 } 33 } 34 35 }
6、2 IDisposable模式
继承这个接口,并重写指定的方法 void Dispose();
在这个方法中,执行一些释放操作。
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 TemporaryFileStream fileStream = new TemporaryFileStream(); 6 //Use temporary file stream; 7 8 //.. 9 10 fileStream.Dispose(); 11 12 //.. 13 14 15 16 } 17 } 18 19 public class TemporaryFileStream : IDisposable 20 { 21 private readonly FileStream _Stream; 22 public FileStream Stream 23 { 24 get { return _Stream; } 25 } 26 private FileInfo _File = new FileInfo(Path.GetTempFileName()); 27 public FileInfo File 28 { 29 get { return _File; } 30 } 31 32 public TemporaryFileStream() 33 { 34 _File = new FileInfo(Path.GetTempFileName()); 35 _Stream = new FileStream(File.FullName, FileMode.OpenOrCreate, FileAccess.ReadWrite); 36 } 37 ~TemporaryFileStream() 38 { 39 Close(); 40 } 41 public void Close() 42 { 43 if (Stream != null) 44 { 45 Stream.Close(); 46 } 47 if (File != null) 48 { 49 File.Delete(); 50 } 51 // Turn off calling the finalizer 52 System.GC.SuppressFinalize(this); 53 } 54 public void Dispose() 55 { 56 Close(); 57 } 58 59 }
6、3 使用using语句进行确定性终结
1 static void Search() 2 { 3 using (TemporaryFileStream fileStream1 = new TemporaryFileStream(), fileStream2 = new TemporaryFileStream()) 4 { 5 //出了这个范围,会自动被释放 6 } 7 }
在using语句内,可以实例化多个变量,只需用逗号分隔每个变量就可以。
7、垃圾回收与终结
System.GC.SuppressFinalize()。
它的作用是从终结队列(f-reachable)队列中移除指定的引用实例。
f-reachable 队列是准备好进行垃圾回收,同时实现终结的所有对象的一个列表。
假如一个对象有终结器,那么“运行时”只有在对象的终结方法被调用之后,才能对这个对象执行垃圾回收。
然而,垃圾回收本身不会调用终结方法。相反,对这种对象的引用会添加到f-reachable队列中,从而在事实上推迟了垃圾回收。
这是由于 f-reachable队列是一个“引用”列表,一个对象只有在它的终结方法得到调用,而且对象引用从f-reachable队列中删除
之后,才会真正成为“垃圾”。
注:对象复活
调用一个对象的终结方法时,对该对象的引用都已经消失,而且在垃圾回收之前,剩下的唯一一个步骤就是运行终结代码。
然而,完全有可能不慎重新引用一个待终结的对象。这样一来,被重新引用 的对象就不再是不可访问的。
所以,它不能当作垃圾回收掉。然而,假如对象的终结方法已经运行,那么除非显式标记为要进行终结(使用
GC.ReRegisterFinalize()方法),否则终结方法不一定会再次执行。
资源利用和终结的指导原则
定义要对资源进行管理的类时,你应该注意以下几点。
只有在对象使用了稀缺或昂贵资源的前提下,才为对象实现finalize。终结器会推迟垃圾回收。
有终结器的对应应该实现IDisposable接口来支持确定性终结。
终结方法通常调用与IDisposable调用相同的代码、可能就是调用Dispose()方法。
终结器就避免造成任何未处理的异常。
像Dispose()和Close()这样的确定性终结方法应该调用System.GC.SuppressFinalize(),使垃圾回收更快地发生,
并避免重复进行资源清理。
负责资源清理的代码可以多次调用,所以应该是可以重入的。
资源清理方法应该足够简单,而且只应着重于清理由终结实例引用 的资源。它们不应引用其他对象。
若基类实现了Dispose(),则派生实现应调用基类的实现
应该确保在调用了Dispose()之后,对象不能继续使用。
8、延迟初始化
不在声明和构造函数中初始化,在属性中get初始化。
1 class DataCache 2 { 3 private TemporaryFileStream _FileStream = null; 4 public TemporaryFileStream FileStream 5 { 6 get 7 { 8 if (_FileStream == null) 9 { 10 _FileStream = new TemporaryFileStream(); 11 } 12 return _FileStream; 13 } 14 } 15 16 }
9、为泛型和lambda表达式使用推迟加载
1 class DataCache 2 { 3 4 //为泛型和lambda表达式使用推迟加载(C#4.0 CLR添加了一个新类来帮助进行推迟初始化:System.Lazy<T> 5 private Lazy<TemporaryFileStream> _FileStream = null; 6 7 public DataCache() 8 { 9 _FileStream = new Lazy<TemporaryFileStream>(()=> new TemporaryFileStream() ); 10 } 11 public TemporaryFileStream FileStream 12 { 13 get 14 { 15 return _FileStream.Value; 16 } 17 } 18 19 }
System.Lazy<T> 获取一个参数(T),这个参数标识了System.Lazy<T> 的Value属性要返回的类型。不是将
一个完全构造好的TemporaryFileStream 赋给_FileStream字段。相反,赋给它的是Lazy<TemporaryFileStream>的一个
实例(一个轻量级的调用),将TemporaryFileStream 本身的初始化推迟到访问Value属性的时候才进行(进而访问FileStream属性)
如果除了类型参数(泛型)还使用了委托,甚至可以提供一个函数来指定在访问Value属性值时如何初始化一个对象。
要注意的是:Lambda表达式本身。即()=> new TemporaryFileStream() 是直到调用Value时才执行的。
Lambda表达式提供了一种方式为将来发生的事情传递指令,但除非显式请求,否则那些指令是不会执行的。