博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
《CLR Via C# 第3版》笔记之(十五) - 接口
阅读量:7104 次
发布时间:2019-06-28

本文共 5828 字,大约阅读时间需要 19 分钟。

接口(interface)和类(class)是CLR中应用最为广泛的两个概念。灵活的应用接口,可以构造出各种经典的设计模式。

接口的语法并不复杂,本篇主要记录接口中一些容易忽略的地方,以及如何更好的使用接口。

主要内容:

  • 接口的继承
  • 显式接口
  • 泛型接口和约束
  • 接口和抽象类 

1. 接口的继承

当子类继承父类后,父类继承的接口也一并继承了过来。如下例中的类Sub

当子类继承父类后,子类可以再次继承父类已经继承的接口。如下例中的类Sub2

这两者的区别在对接口方法调用,参见下面代码中的注释。

using System;sealed class CLRviaCSharp_15{    static void Main(string[] args)    {        Sub s = new Sub();        // 类Sub自身的Show方法        s.Show();        // 类Base的Show方法,即继承自IShowMessage的Show方法        ((Base)s).Show();        // 类Base继承自IShowMessage的Show方法        ((IShowMessage)s).Show();        Console.WriteLine("=============================");        Sub2 s2 = new Sub2();        // 类Sub2自身的Show方法,即继承自IShowMessage的Show方法        s2.Show();        // 类Base的Show方法        ((Base)s2).Show();        // 类Sub2继承自IShowMessage的Show方法        ((IShowMessage)s2).Show();        Console.ReadKey();    }}interface IShowMessage{    void Show();}class Base : IShowMessage{    public void Show()    {        Console.WriteLine("IShowMessage");    }}/// /// 当子类继承父类后,父类继承的接口也一并继承了过来/// class Sub : Base{    // 类Sub本身的Show方法,与Base中继承IShowMessage的Show无关    public new void Show()    {        Console.WriteLine("Sub");    }}/// /// 子类继承父类后,子类可以再次继承父类已经继承的接口/// class Sub2 : Base, IShowMessage{    // 类Sub2继承IShowMessage的Show方法,    // 与Base中继承IShowMessage的Show无关    public new void Show()    {        Console.WriteLine("Sub2");    }} 

2. 显式接口

在上例中,类Base继承IShowMessage之后,就不能定义与 IShowMessage中签名相同的方法了。

如上例中,类Base无法再定义与方法Show相同签名的方法了。

为了解决这种情况,C#中还提供了显式接口的定义方法。

class Base : IShowMessage{    public void Show()    {        Console.WriteLine("Base");    }        void IShowMessage.Show()    {        Console.WriteLine("IShowMessage");    }}

这样,如果要调用IShowMessage.Show()方法,必须将Base类的实例转型成IShowMessage才行。

Base b = new Base();        b.Show();        ((IShowMessage)b).Show();

显示接口的作用主要如下:

1. 当两个接口有签名相同的方法时,一个类可以通过显示接口的方式来同时继承这两个接口。

interface IShowMessage{    void Show();}interface IPrintMessage{    void Show();}class Base : IShowMessage, IPrintMessage{    public void Show()    {        Console.WriteLine("Base");    }        void IShowMessage.Show()    {        Console.WriteLine("IShowMessage");    }    void IPrintMessage.Show()    {        throw new NotImplementedException();    }}

2. 通过显式接口来增强类型安全性,从而减少装箱操作,提高性能

首先是实现隐式接口的例子:

using System;sealed class CLRviaCSharp_15{    static void Main(string[] args)    {        Base b = new Base();        b.Show(10);        Console.ReadKey();    }}interface IShowMessage{    void Show(Object o);}class Base : IShowMessage{    #region IShowMessage Members    public void Show(object o)    {        Console.WriteLine(o.ToString());    }    #endregion}

在调用b.Show(10)时发生装箱操作,通过以下的IL代码(IL_00a)可以看出

.method private static hidebysig 	void Main (		string[] args	) cil managed {	// Method begins at RVA 0x217c	// Code size 28 (0x1c)	.maxstack 2	.entrypoint	.locals init (		[0] class Base b	)	IL_0000: nop	IL_0001: newobj instance void Base::.ctor()	IL_0006: stloc.0	IL_0007: ldloc.0	IL_0008: ldc.i4.s 10	IL_000a: box int32	IL_000f: callvirt instance void Base::Show(object)	IL_0014: nop	IL_0015: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()	IL_001a: pop	IL_001b: ret} // End of method CLRviaCSharp_15.Main

通过显示实现接口,可以在类Base中定义输出int型的Show方法,代码如下:

using System;sealed class CLRviaCSharp_15{    static void Main(string[] args)    {        Base b = new Base();        b.Show(10);        Console.ReadKey();    }}interface IShowMessage{    void Show(Object o);}class Base : IShowMessage{    public void Show(int i)    {        Console.WriteLine(i.ToString());    }    #region IShowMessage Members    void IShowMessage.Show(object o)    {        throw new NotImplementedException();    }    #endregion}

查看Main函数的IL代码,已经没有了之前的装箱操作

.method private static hidebysig 	void Main (		string[] args	) cil managed {	// Method begins at RVA 0x217c	// Code size 23 (0x17)	.maxstack 2	.entrypoint	.locals init (		[0] class Base b	)	IL_0000: nop	IL_0001: newobj instance void Base::.ctor()	IL_0006: stloc.0	IL_0007: ldloc.0	IL_0008: ldc.i4.s 10	IL_000a: callvirt instance void Base::Show(int32)	IL_000f: nop	IL_0010: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()	IL_0015: pop	IL_0016: ret} // End of method CLRviaCSharp_15.Main

显式接口还有一点需要注意的地方是,显式接口不能由派生类调用。

3. 泛型接口和约束

泛型是之前看过的概念,在接口中使用泛型,同样可以获得泛型带给我们的种种好处。

1. 提供类型安全性

比如IComparable接口和IComparable<Int32>接口相比,后者提供了类型安全检查。

int x = 1;        IComparable c1 = x;        // 编译通过,但是运行时异常        c1.CompareTo("2");        IComparable
c2 = x; // 编译不通过,类型不匹配 c2.CompareTo("2");

2. 减少装箱次数

还是用IComparable接口和IComparable<Int32>接口做比较。

int x = 1, y = 2;        // 此处装箱一次 (x装箱)        IComparable c1 = x;        // 此处装箱一次 (y装箱)        c1.CompareTo(y);        // 此处装箱一次 (x装箱)        IComparable
c2 = x; // 此处不用装箱,因为类型参数T为Int32 c2.CompareTo(y);

3. 一个类可以实现同泛型参数类型不同的同一个接口,如下所示

class MyClass : IComparable
,IComparable
{ #region IComparable
Members public int CompareTo(int other) { throw new NotImplementedException(); } #endregion #region IComparable
Members public int CompareTo(string other) { throw new NotImplementedException(); } #endregion}

4. 接口和基类

“接口和基类的关系,以及何时使用接口,何时使用基类。”是在设计时经常需要考虑的问题。

已经有很多文章对此进行了讨论,这里要说的是这两件事可以同时做。

即:定义一个接口,同时提供一个实现了这个接口的基类

.net Framework中的就有这样的例子,比如IComparable接口就有Comparable类提供的默认实现。

比如下面的例子,即使MyClass中没有实现IShowMessage的代码也无所谓,因为ShowMessage提供了默认实现。

interface IShowMessage{    void Show(Object o);}class ShowMessage : IShowMessage{    #region IShowMessage Members    public void Show(object o)    {        Console.WriteLine(o.ToString());    }    #endregion}class MyClass : ShowMessage , IShowMessage{}

转载于:https://www.cnblogs.com/wang_yb/archive/2011/07/28/2119737.html

你可能感兴趣的文章
再说“使用CI操作oracle 10g的单表增删改查”
查看>>
普及一点平板电脑知识,给机油们说说国产平板的五大主流方案
查看>>
js正则实现用户输入银行卡号的控制及格式化
查看>>
MySQL
查看>>
2018-08-14期 Zookeeper客户端连接工具ZooInspector使用方法
查看>>
unity3D初识对象池技术
查看>>
emulator-arm.exe停止工作
查看>>
spring用动态代理还是cglib?
查看>>
共同抵制恶意APP CNCERT公布首批黑名单
查看>>
STP工作过程分析<欢迎指正>
查看>>
您是在为这些问题苦恼吗?
查看>>
几种http-equiv
查看>>
gnuplot绘制曲线图
查看>>
静态缓存
查看>>
使用mdadm创建软raid
查看>>
网络传输之数据单位 kbpsKB/s
查看>>
jquey写的简单图片轮转
查看>>
《JavaScript权威指南》代码解读 -- 第9章:类和模块 (导论)
查看>>
字符测试 =~ 用法
查看>>
【OSChina-MoPaaS应用开发大赛】BiLi记事本
查看>>