发布日期:2009.03.05 作者:Anytao 
© 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处。

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

www.anytao.com

 

1 缘起

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

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

观点一:

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

观点二:

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

// Release : code01, 2009/03/04
// Author : Anytao, http://www.anytao.com
// List : IObjectable.cs
public interface IObjectable
{
}
// Release : code02, 2009/03/04
// Author : Anytao, http://www.anytao.com
// List : MyObject.cs
public class MyObject : IObjectable
{
}
那么,对于IObjectable对象而言,下面的调用是可行的:
// Release : code03, 2009/03/04
// Author : Anytao, http://www.anytao.com
// List : Program.cs
class Program
{
static void Main(string[] args)
{
IObjectable obj = new MyObject(); //Call Object instance methods
obj.ToString();
//Call Object static methods
IObjectable.Equals(null, null);
}
}

显然,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这样的标识:

.class public interface abstract auto ansi ICloneable
{
.custom instance void System.Runtime.InteropServices.ComVisibleAttribute::.ctor(bool) = { bool(true) }
.method public hidebysig newslot abstract virtual instance object Clone() cil managed
{
}
}
自定义类型也是如此,我们看看IObjectable的IL反编译定义:
.class public interface abstract auto ansi IObjectable
{
}
而以extends标识继承关系是IL代码告诉我们真相的最佳证明。

System.Object真是“万物归宗”吗?

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

/NOAUTOINHERIT  Disable inheriting from System.Object by default

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

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

.class public auto ansi beforefieldinit Anytao.Insidenet.InterfaceInside.MyObject
extends [mscorlib]System.Object
implements Anytao.Insidenet.InterfaceInside.IObjectable
{
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method MyObject::.ctor } // end of class Anytao.Insidenet.InterfaceInside.MyObject

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

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

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

4 结论

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

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

回答观点一:

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

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

回答观点二:

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

// Release : code02, 2009/03/04
// Author : Anytao, http://www.anytao.com
// List : MyObject.cs
public class MyObject : IObjectable
{
}

其在本质上相当于:

// Release : code02, 2009/03/04
// Author : Anytao, http://www.anytao.com
// List : MyObject.cs
public class MyObject : Object, IObjectable
{
}

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

好了,interface,想说爱你不容易,可能我们还会再次相遇,也敬请朋友们继续关注:你必须知道的.NET

补充讨论:Leo ZhangInterface到底继承于Object吗?之我见

友情支持:脑袋飞林沙 、博客园北京俱乐部


anytao | © 2009 Anytao.com

2009/03/05 | http://anytao.cnblogs.com/ |http://www.cnblogs.com/anytao/archive/2009/03/05/must_net_27.html

本文以“现状”提供且没有任何担保,同时也没有授予任何权利。 | This posting is provided "AS IS" with no warranties, and confers no rights.

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

参考文献

[你必须知道的.NET]第二十七回:interface到底继承于object吗?的更多相关文章

  1. [你必须知道的.NET]第二十回:学习方法论

    说在,开篇之前 本文,源自我回答刚毕业朋友关于.NET学习疑惑的回复邮件. 本文,其实早计划在<你必须知道的.NET>写作之初的后记部分,但是因为个中原因未能如愿,算是补上本书的遗憾之一. ...

  2. [你必须知道的.NET]第二十一回:认识全面的null

    发布日期:2008.7.31 作者:Anytao © 2008 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 null.nullable.??运算符.null ...

  3. [你必须知道的.NET]第二十三回:品味细节,深入.NET的类型构造器

    发布日期:2008.11.2 作者:Anytao © 2008 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 今天Artech兄在<关于Type Init ...

  4. [你必须知道的.NET]第十七回:貌合神离:覆写和重载

    本文将介绍以下内容: 什么是覆写,什么是重载 覆写与重载的区别 覆写与重载在多态特性中的应用 1. 引言 覆写(override)与重载(overload),是成就.NET面向对象多态特性的基本技术之 ...

  5. [你必须知道的.NET]第二十四回:认识元数据和IL(上)

    发布日期:2009.02.24 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 很早就有说说Metadata(元数据)和IL(中 ...

  6. [你必须知道的.NET] 第八回:品味类型---值类型与引用类型(上)-内存有理

    原文地址:http://kb.cnblogs.com/page/42318/ 系列文章导航: [你必须知道的.NET] 开篇有益 [你必须知道的.NET] 第一回:恩怨情仇:is和as [你必须知道的 ...

  7. [你必须知道的.NET]第二十六回:认识元数据和IL(下)

    发布日期:2009.03.04 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 书接上回: 第二十四回:认识元数据和IL(上), ...

  8. [你必须知道的.NET]第二十五回:认识元数据和IL(中)

    发布日期:2009.02.25 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 书接上回[第二十四回:认识元数据和IL(上)], ...

  9. [你必须知道的.NET]第二十九回:.NET十年(上)

    发布日期:2009.05.08 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. /// <summary> /// 本文部分内容,已 ...

随机推荐

  1. How to speed up insertion performance in PostgreSQL

    Disable any triggers on the table Drop indexes before starting the import, re-create them afterwards ...

  2. maven中用yuicompressor和closure-compiler对js、css文件进行压缩

    转载自:http://matychen.iteye.com/blog/1477350 项目采用maven构建的时候,需要压缩js,css等,网上找了相关资料,自己综合了下-  直接放代码: <! ...

  3. 使用 html2canvas 实现浏览器截图

    基于上一篇<h5 本地上传图片预览 源码下载>,今天分享一个图片上传后, 根据所上传的图片颜值随机生成一个答案, 并且可以生成一张专属于自己的名片. 首先上传预览我们已经实现了, 所以接下 ...

  4. Django ORM常用的函数以及修饰词

    函数名称或修饰词 说明 filter() 返回符合指定条件的QuerySet exclude() 返回不符合指定条件的QuerySet ordey_by() 串接到QuerySet之后,针对某一指定的 ...

  5. [Unity]模拟雨水的折射效果

    用GrabPass做的小玩具. 并不是真的计算了折射,只是简单地扰动了uv,对于雨水来说效果已经足够好了. Shader代码: Shader "Unlit/Rain" { Prop ...

  6. [POI2004] SZP (贪心+拓扑排序)

    [问题描述] Byteotian 中央情报局(BIA) 雇佣了许多特工. 他们每个人的工作就是监视 另一名特工. Byteasar 国王需要进行一次秘密行动,所以他要挑选尽量多的信得过的特工. 但 是 ...

  7. webpack_配置和使用教程

    webpack是一个模块打包的工具,它的作用是把互相依赖的模块处理成静态资源. webpack 可以使用 loader 来预处理文件.这允许你打包除 JavaScript 之外的任何静态资源.你可以使 ...

  8. Port-knocking 简单教程

    0. "port knocking" 如字面意思,类似'敲门',只是这里敲的是'端口',而且需要按照顺序'敲'端口.如果敲击规则匹配,则可以让防火墙实时更改策略.从而达到开关防火墙 ...

  9. Perl6 Bailador框架(3):路径匹配

    use v6; use Bailador; =begin pod 注意的是, 当/:one设置时 虽然你有/admin或/about, 但这个/:one不会跟现有的匹配 只跟没有的匹配: 也就是说, ...

  10. bisai2.py

    比赛专用py #!/usr/bin/env python #encoding:utf- #by i3ekr import re,requests,os res = "(flag{.*?}&q ...