鼎鼎知识库
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

20.C#继承.md 5.4KB

před 5 roky
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. > 继承的类型
  2. - 单重继承:表示一个类可以派生自一个基类。C#就采用这种继承。
  3. - 多重继承:多重继承允许一个类派生自多个类。C#不支持类的多重继承,但允许接口的多重继承。
  4. - 多层继承:多层继承允许继承有更大的层次结构。类B派生自类A。类C又派生自类B。其中,类B也称为中间基类,C#支持它,也很常用
  5. - 接口继承:定义了接口的继承。这里允许多重继承。
  6. >实现继承
  7. - 声明派生自另一个类的一个类,可以用下列语法:
  8. ```
  9. class MyDerivedClass:MyBaseClass
  10. {
  11. //
  12. }
  13. ```
  14. - 如果类也派生自接口,则用逗号分隔列表中的基类和接口:
  15. ```
  16. public class MyDerivedClass:MyBaseClass,IInterfacel,IInterface2
  17. {
  18. //
  19. }
  20. ```
  21. - 如果类和接口都用于派生,则类总是必须放在接口的前面。
  22. - 对于结构,语法如下(只能用于接口继承):
  23. ```
  24. public struct MyDerivedStruct:IInterfacel,IInterface2
  25. {
  26. //
  27. }
  28. ```
  29. - 如果在类定义中没有指定的基类,C#编译器就假定System.Object 是基类。因此,派生自Object类(或使用object关键字),与不定义基类的效果是相同的。
  30. ```
  31. class MyClass //implicitly derives from System.Object
  32. {
  33. }
  34. ```
  35. 举个例子,无论是矩形还是椭圆,形状都有一些共同点;形状都有位置和大小。定义相应的类时,位置和大小应包含在Shape类中。Shape类定义了只读属性Position和Shape,它们使用自动属性初始化器来初始化
  36. ```
  37. public class Position
  38. {
  39. public int X{get;set;}
  40. public int Y{get;set;}
  41. }
  42. public class Size
  43. {
  44. public int Width{get;set;}
  45. public int Heigh{get;set;}
  46. }
  47. public class Shape
  48. {
  49. public Position Position{get;}=new Position();
  50. public Size Size{get;}=new Size();
  51. }
  52. ```
  53. > 虚方法
  54. - 把一个基类方发声明为virtual,就可以在任何派生类中重写该方法
  55. ```
  56. public class Shape
  57. {
  58. public virtual void Draw()
  59. {
  60. WriteLine($"Shape with {Position}and{size}");
  61. }
  62. }
  63. ```
  64. - 也可以把virtual 关键字和表达式体的方法(使用Lamda运算符)一起使用。这个语法可以独立修饰符,单独使用
  65. ```
  66. public class Shape
  67. {
  68. public virtual void Draw()=>WriteLine($"Shape with {Position}and{size}")
  69. }
  70. ```
  71. - 也可以把属性声明为virtual。对于虚属性或重写属性,语法与非虚属性相同,但要在定义中添加关键字virtual,其语法如下
  72. ```
  73. public virtual Size Size{get;set;}
  74. 可以给虚属性使用完整的属性语法:
  75. private Size _size;
  76. public virtual Size Size
  77. {
  78. get
  79. {
  80. return _size;
  81. }
  82. set
  83. {
  84. _size=value;
  85. }
  86. }
  87. ```
  88. - Size和Position类型重写了ToString()方法。这个方法在基类Object中声明virtual:
  89. ```
  90. public class Position
  91. {
  92. public int X{get;set;}
  93. public int Y{get;set;}
  94. public override string ToString()=>$"X:{X},Y:{Y}";
  95. }
  96. public class Size
  97. {
  98. public int Width{get;set;}
  99. public int Height{get;set;}
  100. public override string ToString()=>$"Width:{Width},Height:{Height}";
  101. }
  102. ```
  103. > is和as运算符
  104. - is和as是与继承有关的重要运算符
  105. > using
  106. - using关键字在C#中用多个用法,using声明用于导入名称空间。using语句处理实现IDisposable的对象,并在作用域的末尾用Dispose方法。
  107. > 实现IDisposable接口和析构函数
  108. - 利用运行库强制执行的析构函数,但析构函数的执行是不确定的,而且,由于垃圾回收器的工作方式,它会给运行库增加不可接受的系统开销。
  109. - IDisposable 接口提供一种机制,该机制允许类的用户控制释放资源的时间,但需要确保调用Dispose()方法。
  110. - 如果创建了终结器,就应该实现IDisposable接口。
  111. - 双重实现的例子
  112. ```
  113. using System;
  114. public class ResourceHolder:IDisposable
  115. {
  116. private bool _isDisposed=false;
  117. public void Dispose()
  118. {
  119. Dispose(true);
  120. GC.SuppressFinalize(this);
  121. }
  122. protect virtual void Dispose(bool disposing)
  123. {
  124. if(!_isDisposed)
  125. {
  126. if(disposing)
  127. {
  128. }
  129. }
  130. }
  131. _isDisposed=true;
  132. }
  133. -ResourceHolder()
  134. {
  135. Dispose(false);
  136. }
  137. public void SomeMethod()
  138. {
  139. if(_isDisposed)
  140. {
  141. throw new ObjectDisposedException("ResourceHolder");
  142. }
  143. }
  144. Dispose()方法有第二个protect的重载方法,它带一个布尔参数,这是真正完成清理工作的方法。Dispose(bool)方法由析构函数和IDisposable.Dispose()方法调用。
  145. ```
  146. >IDisposable和终结器的规则
  147. - 如果类定义了实现IDisposable的成员,该类也应该实现IDisposable
  148. - 实现IDisposable并不意味着也应该实现一个终结器。终结器会带来额外的开销,因为它需要创建一个对象,释放该对象的内存,需要GC的额外处理。只在需要时才应该实现终结器,例如,发布本机资源。要释放本机资源,就需要终结器
  149. - 如果实现了终结器,也应该实现IDisposable接口。这样,本机资源可以早些释放,而不仅实在GC找出被占用的资源时,才释放资源。
  150. - 在终结器的实现代码中,不能访问已终结的对象。终结器的执行顺序是没有保证的
  151. - 如果所使用的一个对象实现了IDisposable接口,就不再需要对象时调用Dispose方法。如果在方法中使用这个对象,using语句比较方便。如果对象是类的一个成员,就让类也实现IDisPOSable。