- 本博客所总结书籍为《CLR via C#(第4版)》清华大学出版社,2021年11月第11次印刷(如果是旧版书籍或者pdf可能会出现书页对不上的情况)
- 你可以理解为本博客为该书的精简子集,给正在学习中的人提供一个“glance”,以及对于部分专业术语或知识点给出解释/博客链接。
- 【本博客有如下定义“Px x”,第一个代表书中的页数,第二个代表大致内容从本页第几段开始。(如果有last+x代表倒数第几段,last代表最后一段)】
- 电子书可以在博客首页的文档-资源归档中找到,或者点击:传送门自行查找。如有能力请支持正版。(很推荐放在竖屏上阅读本电子书,这多是一件美事)
- 欢迎加群学习交流:637959304 进群密码:(CSGO的拆包密码)
第十三章 接口
- CLR不支持多继承,CLR只通过接口提供了“缩水版”的多继承。
类和接口继承
- Microsoft .NET Framework提供了System.Object类,它定义了4个公共实例方法:ToString,Equals,GetHashCode和 GetType。该类是其他所有类的根或者说终极基类。换言之,所有类都继承了Object的4个实例方法。这还意味着只要代码能操作Object类的实例,就能操作任何类的实例。(P259 last2)
从Object派生的任何类都继承了:1、方法签名 2、方法实现
定义接口
- 接口:CLR还允许开发人员定义接口:接口实际只是对一组方法签名进行了统一命名。这些方法不提供任何实现。类通过指定接口名称来继承接口,而且必须显式实现接口方法,否则CLR会认为此类型定义无效。C#编译器和CLR允许一个类继承多个接口,继承的所有接口方法都必须实现。(P260 2)
- 接口能够定义事件、无参属性,有参数性(C#的索引器),静态方法,静态字段,常量和静态构造器。但不允许定义符合CLS标准的接口,因为有的编程语言不能定义或访问他们(P260 3)
- C#用interface定义接口。根据约定,接口类型名称以大写字母I开头,这样方便在源代码中辨认接口类型。CLR支持泛型接口和接口中的泛型方法。(P261 2)
继承接口
- C#编译器要求将实现接口的方法(后文简称为“接口方法”)标记为 public。CLR要求将接口方法标记为 virtual。不将方法显式标记为 virtual,编译器会将它们标记为 virtual和sealed;这会阻止派生类重写接口方法。将方法显式标记为 virtual,编译器就会将该方法标记为virtual(并保持它的非密封状态),使派生类能重写它。(P262 1)
- 派生类不能重写sealed 的接口方法。但派生类可重新继承同一个接口,并为接口方法提供自己的实现。在对象上调用接口方法时,调用的是该方法在该对象的类型中的实现。(P262 2)
关于调用接口方法的更多探讨
- FCL的System.String类型(可以看一下书中代码示例)(P263 last2)
隐式和显示接口方法实现(幕后发生的事情)
- C#编译器为支持接口所做的工作(P265 1)
- 在C#中,将定义方法的那个接口的名称作为方法名前缀(例如 IDisposable.Dispose),就会创建显式接口方法实现(Explicit Interface Method Implementation,EIMI)。定义时不能标记为vitual,不能指定可访问性。(P265 last)
泛型接口
- 泛型接口的好处:1、提供了出色的编译时类型安全性。2、处理类型时装箱次数少 3、类可以实现同一个接口若干次,只要每次使用不同的类型参数。(P266 3)
private void fun()
{
int x = 1,y = 2;
IComparable<int> c = x;//泛型接口<int>
c.CompareTo(y);//y不在这里装箱
c.CompareTo("2");//错误,提示string不能转int
}
泛型和接口约束
- 泛型接口约束的好处:1、可将泛型类型参数约束为多个接口。如此,传递的参数类型必须实现全部接口约束。 2、传递值类型的实例时减少装箱(P268 2)
实现多个具有相同方法名和签名的接口
- 实现定义两个接口的类型,那么必须实现这些接口的方法,因此要告诉C#编译器每个同名方法对应的是哪个接口的实现。(P269 last2)
public interface IWindow
{
Object GetMEenu();
}
public interface IRestaurant
{
Object GetMEenu();
}
public sealed class fun : IWindow,IRestaurant
{
Object IWindow.GetMEenu(){}
Object IRestaurant.GetMEenu(){}
public Object GetMEenu(){}//该方法可选与接口无关
}
用显式接口方法实现来增强编译时类型安全性
- 有时由于不存在泛型版本,所以仍需实现非泛型接口。接口的任何方法接受System.Object类型的参数或返回System.Object类型的值,就会失去编译时的类型安全性,装箱也会发生。可以用显式接口方法在某种程度上改善这个问题。(P270 1)
public interface IComparable
{
int CompareTo(Object other);
}
internal struct fun : Icomparable
{
private int m_x;
public fun(int x) {m_x = x}
public int CompareTo(Object other)
{
return (m_x-((fun)other).m_x);
}
}
public static void Main()
{
fun v = new fun(0);
Object o = new Object();
int n = v.CompareTo(v);//不希望的装箱操作
n = v.CompareTo(o);//InvalidCastException异常
}
- 上述代码存在问题:1、不希望的装箱操作 2、缺乏类型安全性(P270 last)
//EIMI修改后版本
internal struct fun : IComparable
{
private int m_x;
public fun(int x) {m_x = x}
public int CompareTo(Object other)
{
return (m_x-((fun)other).m_x);
}
//定义接口类型的变量会再次失去编译时的类型安全性,且会再次发生装箱
int IComparable.CompareTo(Object other)
{
return CompareTo((fun)other);
}
}
谨慎使用显示接口方法实现
- EIMI问题:1、没有文档解释类型具体如何实现一个EIMI方法名也没有visual studio只能感知支持。2、值类型的实例在转换成接口时装箱3、EIMI不能由派生类调用(P271 last)
设计:基类还是接口
- 基类还是接口的选择思路:1、IS-A对比CAN-DO关系(IS-A代表属于,a属于b。CAN-DO代表能做某事,类型能将自己的实例转化为另一个类型)2、易用性 3、一致性实现 4、版本控制(P274 1)