关于interface继承来源的讨论

开发 后端
关于interface继承来源的讨论主要是interface继承是否与System.Object有关系的内容,那么本文就向你介绍相关的一些观点,希望对你了解和学习有所帮助。

在.NET世界里,我们常常听到的一句话莫过于“System.Object是一切类型的根,是所有类型的父类”,以至于我在《你必须知道的.NET》8.1节 以“万物归宗:System.Object”这样的title为System.Object授予至高荣誉。所以,基于这样的观点就有了下面这句“接口是否也继承于System.Object?”,事实上这正是今天在技术群里小小讨论的一个插曲。

持“interface也继承于object”,是基于以下的两个观点推断的:

观点一:

接口本质上也是一个class,因为接口类型编译之后在IL中被标识为.class,既然是类那么不可避免的最终继承于System.Object。

观点二:

假如有如下的接口和实现接口的类型:

  1. // Release : code01, 2009/03/04                      
  2. // Author  : Anytao  
  3. // List    : IObjectable.cs  
  4. public interface IObjectable  
  5. {  
  6. }// Release : code02, 2009/03/04                      
  7. // Author  : Anytao  
  8. // List    : MyObject.cs  
  9. public class MyObject : IObjectable  
  10. {  

那么,对于IObjectable对象而言,下面的调用是可行的:

  1. // Release : code03, 2009/03/04                      
  2. // Author  : Anytao  
  3. // List    : Program.cs  
  4. class Program  
  5. {  
  6.     static void Main(string[] args)  
  7.     {  
  8.         IObjectable obj = new MyObject();  
  9.  
  10.         //Call Object instance methods  
  11.         obj.ToString();  
  12.         //Call Object static methods  
  13.         IObjectable.Equals(nullnull);  
  14.     }  
  15. }  

显然,IObjectable类型变量obj可以访问存在于System.Object中的实例方法ToString()和虚方法Equals,当然其他的几个公共服务也不例外:GetType()、Equals()、GetHashcode()、ReferenceEquals(),也可以由此推断interface可访问Object方法的蛛丝马迹。

不可否认,以上观点的部分推理是完全正确的,但是却遗憾的导致了错误的答案,所以在本文中我将明确的找出:interface不继承于object的原因和原理。关于接口本质话题的深度讨论,请参考《你必须知道的.NET》1.5 “玩转接口”和7.4 “面向抽象编程:接口和抽象类”的详细分析。

2 从面向对象寻找答案

为了找出接口继承的原因,我想从接口存在的意义入手是最能够说明问题的办法?接口,就像面向对象设计中的精灵,为OO思想注入了灵魂和活力,接口突破了继承在纵向上的扩展方向,在横向给予对象以更灵活的支持机制。

接口,封装了对于行为的抽象,定义了实现者必须遵守的契约。例如,实现了System.ICloneable接口的类型被赋予了“可以被拷贝”这样的契约,实现了System.Collections.IEnumerable接口的类型被赋予了“可以被枚举”这样的契约,不同的接口定义了不同的契约,就像不同的法律约束了不同的行为。那么接口应该赋予的契约至少在层次上保持相对的单纯和统一,如果为所有接口都无一例外的赋予GetType()、Equals()、GetHashcode()、ReferenceEquals()还有ToString()这样的契约,未免使得接口的纯洁和统一变得无从谈起,例如强迫任何实现了System.ICloneable接口的类型同时遵守其他的约定是对ICloneable本身的侮辱。

从接口单一原则延伸思考,一个包含杂七杂八的接口定义显然不是interface应该具有的纯正血统,对于深谙面向对象为何物的.NET设计者而言,这是不言而喻的问题。所以,我们从接口本身的职责和意义出发,决定interface不从System.Object继承是完全正确的。

3 在IL探求究竟

再次应用强大的IL武器来探求事实的真相,我们以Reflector打开所有的.NET既有接口,例如IList、IEmumerable、ICollection,都会有个共同的发现那就是你找不到extends System.Object这样的标识:

  1. .class public interface abstract auto ansi ICloneable  
  2. {  
  3.     .custom instance void   
  4. System.Runtime.InteropServices.  
  5. ComVisibleAttribute::.ctor(bool) = { bool(true) }  
  6.     .method public hidebysig newslot abstract 
  7.  virtual instance object Clone() cil managed  
  8.     {  
  9.     }  

自定义类型也是如此,我们看看IObjectable的IL反编译定义:

  1. .class public interface abstract auto ansi IObjectable  
  2. {  

而以extends标识继承关系是IL代码告诉我们真相的最佳证明。System.Object真是“万物归宗”吗?

让我们再次回眸一笑,把Object进行一番把玩,难道一切类型都得继承自Object吗?其实不然。以ILASM.exe进行IL代码编译时,有一个参数选项NOAUTOINHERIT,正如其解释所描述的那样:

  1. /NOAUTOINHERIT  Disable inheriting from System.Object by default 

显然NoAutoInherit选项提供了为.NET类型“去掉帽子”的作用,简单言之就是,在未指定基类时,禁止类型自动从Object继承。

我们可以玩儿一个翻来覆去的IL游戏,将我们本文开始的Anytao.Insidenet.InterfaceInside.exe控制台程序以ILDASM.exe工具Dump为IL代码My.il,例如MyObject被反编译为:

  1. .class public auto ansi beforefieldinit   
  2. Anytao.Insidenet.InterfaceInside.MyObject  
  3.        extends [mscorlib]System.Object  
  4.        implements Anytao.Insidenet.InterfaceInside.IObjectable  
  5. {  
  6.   .method public hidebysig specialname rtspecialname   
  7.           instance void  .ctor() cil managed  
  8.   {  
  9.     // Code size       7 (0x7)  
  10.     .maxstack  8  
  11.     IL_0000:  ldarg.0  
  12.     IL_0001:  call         
  13. instance void [mscorlib]System.Object::.ctor()  
  14.     IL_0006:  ret  
  15.   } // end of method MyObject::.ctor  
  16.  
  17. // end of class Anytao.Insidenet.InterfaceInside.MyObject  

我们可以选择删除其中所有extends继承的代码,再以ILASM.exe对其进行noautoinherit编译,并生成

  1. ilasm /exe /output:noobject.exe /noautoinherit my.il 

新生成的noobject.exe程序将没有从object继承,某种程度上打破了“万物归宗”的创奇,MyObject就像一个无根之木,飘摇在我机器的某个深处。

4 结论

interface不从object继承,那么足下高见呢?文章虽短,取一瓢饮之,畅也。

那么,我们该如何回答本文开始对此质疑的两种观点呢?

回答观点一:

接口本质上还是一个类,但是一个特殊的类,它的特殊性表现在诸多的方面,例如所有的方法和属性都是抽象的、支持多继承等等,既然特殊那就特殊到底,不继承于任何的父类也是其中之一吧。

虽然这种解释未免牵强,但是如前文所述回到接口本源的角度而言,却是最好的解释。

回答观点二:

.NET一切类型都隐式继承于System.Object,那么对于实现了任何接口的类型而言,例如:

  1. // Release : code02, 2009/03/04                      
  2. // Author  : Anytao  
  3. // List    : MyObject.cs  
  4. public class MyObject : IObjectable  
  5. {  
  6. }  

其在本质上相当于:

  1. // Release : code02, 2009/03/04                      
  2. // Author  : Anytao  
  3. // List    : MyObject.cs  
  4. public class MyObject : Object, IObjectable  
  5. {  

所以对于MyObject实例obj而言,obj.ToString()实质是MyObject类继承于object,而不代表接口IObjectable也继承于object。那么IObjectable.Equals()则是编译器做了手脚,将IObjectable.Equals()翻译为Object.Equals()所致(来自脑袋高论,表示热烈感谢)。事实上,对于接口声明类型的方法调用,在实现机制上完全不同于一般的直接方法调用和虚方法分派机制。

【编辑推荐】

  1. C#显式实现接口原理浅析
  2. C# interface学习经验浅谈
  3. C# interface使用实例分析
  4. 浅析abstract class和interface的不同
  5. 详解abstract class和interface的本质
责任编辑:仲衡 来源: 博客园
相关推荐

2013-05-20 15:45:12

CSS

2010-09-28 15:42:36

DHCP服务故障排除

2010-07-13 15:36:33

2011-05-19 15:51:54

测试专家

2011-11-02 09:04:15

Node.js

2010-09-28 15:52:08

Cisco路由器DHC

2013-02-28 15:11:56

GitGitHub

2009-10-16 16:11:04

6类布线系统

2010-09-01 09:10:30

DHCP作用域

2017-07-12 16:32:55

2015-05-19 11:11:58

OpenFlowSDN

2023-12-28 16:36:35

大数据

2009-06-18 09:51:25

Java继承

2011-06-16 11:01:56

PHP继承

2011-07-04 16:40:39

QT 串口 QML

2015-06-24 14:29:07

PaaSPaaS困境

2022-09-26 08:26:38

软件定时器函数

2011-06-15 12:58:05

Qt QTableWidg

2011-08-12 10:55:29

客户服务物流平台规划

2014-05-29 10:54:20

C++构造函数
点赞
收藏

51CTO技术栈公众号