一,类继承接口

1,首先我们定义一个简单的ITeacher接口,并定义一个Professor类继承它。

        public interface ITeacher
{
void Print();
} public class Professor : ITeacher
{
public void Print()
{
Debug.Write("I'm Professor!");
}
}
然后用接口变量调用如下:
            ITeacher teacher = new Professor();
teacher.Print();
输出结果:I'm Professor!

2,将Professor类的Print方法改为Virtual结果会怎么样?

        public class Professor : ITeacher
{
public virtual void Print()
{
Debug.Write("I'm Professor!");
}
}

输出结果:I'm Professor!

Virtual的加入,并没有改变接口的行为,但是有了这个关键字,我们可以在子类中重写Professor类的Print方法。

3,定义AssistantProfessor类,继承自Professor类。

        public class AssistantProfessor : Professor
{
public override void Print()
{
Debug.Write("I'm AssistantProfessor!");
}
} ITeacher teacher = new AssistantProfessor();
teacher.Print();

结果输出:I'm AssistantProfessor!

注意,Professor继承了ITeacher接口,它的子类AssistantProfessor无需再显示继承这个接口,子类默认也实现了这个接口,如果子类AssistantProfessor类不实现Print方法,接口会调用父类Professor的Print方法。如果子类AssistantProfessor重写override父类Professor的Print方法,接口会调用子类AssistantProfessor的Print方法,这其实是和类的继承调用一致,用多态可以解释这个原理。

4,在子类AssistantProfessor中使用new关键字,结果会如何?

        public class AssistantProfessor : Professor
{
public new void Print()
{
Debug.Write("I'm AssistantProfessor!");
}
}

上面的代码编译没有任何问题,子类AssistantProfessor的Print方法加了new关键字,代表隐藏父类的Print方法。

而不是父类的重写,这时就没有多态了,接口会调用父类的Print方法,所以结果输出:I'm Professor!。

5,孙子类的Print方法,接口还能调用吗?

        public class Professor : ITeacher
{
public virtual void Print()
{
Debug.Write("I'm Professor!");
}
} public class AssistantProfessor : Professor
{
public override void Print()
{
Debug.Write("I'm AssistantProfessor!");
}
} public class SubAssistantProfessor : AssistantProfessor
{
public override void Print()
{
Debug.Write("I'm SubAssistantProfessor!");
}
} ITeacher teacher = new SubAssistantProfessor();
teacher.Print();

上面的代码,运行结果:I'm SubAssistantProfessor!

这里依然是多态的作用,不知道大家注意到没有,子类AssistantProfessor用了override关键字,孙子类SubAssistantProfessor居然还能用override关键字,不是只有声明为virtual的方法才能override吗?

利用反编译工具,我们可以清晰的看到带有override的方法,被编译后依然是一个虚方法,可以被重写。我想,

这也是多态的意义所在,后辈子类才能表现出各自不同的行为。

	// Methods
.method public hidebysig virtual
instance void Print () cil managed
{
// Method begins at RVA 0x22bb
// Code size 13 (0xd)
.maxstack 8 IL_0000: nop
IL_0001: ldstr "I'm AssistantProfessor!"
IL_0006: call void [System]System.Diagnostics.Debug::Write(string)
IL_000b: nop
IL_000c: ret
} // end of method AssistantProfessor::Print

6,子类显示实现接口

        public interface ITeacher
{
void Print();
} public class Professor : ITeacher
{
public virtual void Print()
{
Debug.Write("I'm Professor!");
}
} public class AssistantProfessor : Professor, ITeacher
{
public new void Print()
{
Debug.Write("I'm AssistantProfessor!");
}
} ITeacher teacher = new AssistantProfessor();
teacher.Print();

上面这个例子和上面的情况4只有一点区别,子类AssistantProfessor显示实现了ITeacher接口。这时,通过ITeacher变量直接找到的是子类AssistantProfessor的Print方法,所以输出结果是:I'm AssistantProfessor!

再看看这段代码:

        public interface ITeacher
{
void Print();
} public class Professor : ITeacher
{
public virtual void Print()
{
Debug.Write("I'm Professor!");
}
} public class AssistantProfessor : Professor, ITeacher
{
}

上面这段代码结果:I'm Professor!

虽然子类定义了Iteacher接口,但是没有提供任何实现。

再来看看这段代码:

        public interface ITeacher
{
void Print();
} public class Professor
{
public virtual void Print()
{
Debug.Write("I'm Professor!");
}
} public class AssistantProfessor : Professor, ITeacher
{
}

这段代码能编译过吗?父类没有实现ITeacher接口,子类实现了Iteacher接口,但却没提供实现方法。

奇怪的是,它能编译通过,运行结果为:I'm Professor!没错!父类实现了ITeacher中定义的方法,子类可以不实现。

上面的一些例子可能有些出乎我们的意料,从中我们也可以大致归纳出接口的调用顺序,去解释那些奇怪的问题。

7,从上面的实验,简单归纳,接口方法执行时的查找顺序:

1) 实例类如果直接继承了接口,则查找实例类上有无接口方法,如果有,直接执行;如果没有,查找实例类父类中有无接口方法,如果有,执行之。(不可能出现找不到的情况,不然编译是通不过的)

2) 实例类如果没有直接继承接口,首先查找实现接口的父类,然后查找父类中有无接口方法,如果有,查看方法是否是virtual方法,如果是,查看实例类有无重写这个方法,如果重写则调用实例类的方法,如果没有重写,直接调用父类的virtual方法。如果在实现接口的父类中没有找到接口方法,则在父类的父类往上查找,直到找到为止,执行该方法。(同理,也不可能出现找不到的情况,不然编译通不过)

二,接口间的继承

1,接口可以继承接口

        public interface ITeacher
{
void Print();
} public interface IGoodTeacher : ITeacher
{
void Read();
}

如果我们要定义一个类来继承IGoodTeacher的话,必须同时实现Print和Read方法,不然会编译出错

        public class Professor : IGoodTeacher
{
public void Print()
{
Debug.Write("I'm Professor! Print");
} public void Read()
{
Debug.Write("I'm Professor! Read");
}
}

2,子类如果继承Professor,也会自动继承IGoodTeacher

        public class AssistantProfessor : Professor
{
}

AssistantProfessor没有提供任何方法,即便AssistantProfessor在显示继承IGoodTeacher接口,也可以不提供任何方法,因为它的父类已经提供了这些方法,这和我们上面讨论的类继承接口的分析方法是一致的,这里就不做进一步的讨论了。

接口与virtual,override,new关键字的更多相关文章

  1. C# virtual,override,new 整理

    今天仔细学习了一下C#中virtual, override, new关键字,参考了网上的很多资料,现整理一下. Virtual: virtual 关键字用于修饰方法.属性.索引器或事件声明,并使它们可 ...

  2. C# virtual override 和 new 的区别

    一直以来我都对 virtual  override 和 new 之间的区别感到疑惑不解. 特别笔试的时候特别容易考到,真的很容易弄错啊,畜生! 光看理论永远记不住,那不如写几行代码就懂了. 首先看看v ...

  3. RobotFramework下的http接口自动化Get Response header 关键字的使用

    Get Response header 关键字用来获取http请求返回的http响应头部数据. 常见的Response Header: Header 解释 示例 Accept-Ranges 表明服务器 ...

  4. RobotFramework下的http接口自动化Get Response Body关键字的使用

    Get Response Body 关键字在上面已经有用到了,服务器端在处理完成了发出的http请求后,会给出对应的响应结果,那么Get Response Body这个关键字就是来获取响应结果中的主体 ...

  5. C#关于private protected sealed Virtual/Override

    Public:公开权限 Private:修饰类时类为程序集或者包含此类的类内部权限:修饰变量时只能类内部使用: Protected:修饰变量,只能继承类可以使用,对外(包括继承类的实例)无权限: Ab ...

  6. Abstract和Virtual和interface , 派生类中重写 override / new关键字

    http://www.cnblogs.com/blsong/archive/2010/08/12/1798064.html C#中Abstract和Virtual 在C#的学习中,容易混淆virtua ...

  7. Modifiers: virtual, override, new, abstract, sealed, internal

    internal 声明类.类成员.接口或接口成员具有内部可见性. internal 修饰符使类.接口或成员仅在当前包中可见. 当前包之外的代码不能访问 internal 成员.只有在同一程序集的文件中 ...

  8. 不要在对抽象类接口abstract virtual什么的混淆不清了

    (最近带一个新手入门 讲这边老费力了.) 接口和抽象类不过都是类的抽象罢了,这是他们的本质,只不过人们的思维角度不同而已,一个偏向为行为契约上的抽象(接口) 一个更像大自然中的繁衍关系(抽象类). 直 ...

  9. C# abstract virtual override new finally java final finalize

    virtual:声明虚方法.可以被其派生类所重写的.重写方法需要使用override或者new关键字. override:覆盖原方法.可对重写virtual.override.abstract进行重写 ...

随机推荐

  1. 引文分析工具HistCite使用简介

    运行环境: win8.1(lenovo Y450) 1.去www.histcite.com下载histcite最新版,并安装 2.去WOS下载文献.保存方式为: 记录数: 记录1至500(最大支持50 ...

  2. MySQL各类日志文件相关变量介绍

    文章转自:http://www.ywnds.com/?p=3721 MySQL各类日志文件相关变量介绍 查询所有日志的变量   1 mysql> show global variables li ...

  3. javascript <a> 标签打开相对路径,绝对路径

    <a>标签中的href中,如果你写一个路径默认是以相对路径打开的,加上"http://" 消息头那就可以打开绝对路径 html: <a href=javascri ...

  4. asp.net 中用easyui中的treegird的简单使用

    几乎每个‘数人头’项目中都会用到的功能,这里先记下来,以后直接到这里复制代码就行了,ASP.NET MVC中的使用 数据库用户表中的除了有个parentid父级ID外,我还多加了以个字段,parent ...

  5. 03.反射--01【反射机制】【反射的应用场景】【Tomcat服务器】

    https://blog.csdn.net/benjaminzhang666/article/details/9408611 https://blog.csdn.net/benjaminzhang66 ...

  6. 基于Swift语言开发微信、QQ和微博的SSO授权登录代码分析

    前言 Swift 语言,怎么说呢,有一种先接受后排斥.又欢迎的感觉,纵观国外大牛开源框架或项目演示,Swift差点儿占领了多半,而国内尽管出现非常多相关技术介绍和教程,可是在真正项目开发中使用的占领非 ...

  7. Word实用教程——五分钟教你如何在任意页开始添加页码

    最近在写一篇论文,但是在排版上遇到一点小问题,就是要加入页码,而且页码是从目录的下一页开始计数,于是我就在网上找如何在任意页添加页码.后来辗转终于搞定,真心觉得这一个小功能让微软做的如此的麻烦,真是活 ...

  8. 每日英语:Researchers Study How Excess Fat Cells Interfere With Organ Function, Metabolism

    Why are some obese people healthy, apparently protected from the damaging effects of excess fat on t ...

  9. RSS Reader in PC & iPhone

    PC上当然是用feedly web版.但出乎意料的是,iPhone上最好用的居然是safari版QQ邮箱...

  10. vue的全局引用

    1 一般在vue中,有很多vue组件,这些组件每个都是一个文件.都可能需要引用到相同模块(或者插件).我们不想每个文件都import 一次模块. 如果是基于vue.js编写的插件我们可以用 Vue.u ...