《CLR Via C# 第3版》笔记之(十五) - 接口

简介:

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

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

主要内容:

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

1. 接口的继承

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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" );
     }
}
 
 
/// <summary>
/// 当子类继承父类后,父类继承的接口也一并继承了过来
/// </summary>
class  Sub : Base
{
     // 类Sub本身的Show方法,与Base中继承IShowMessage的Show无关
     public  new  void  Show()
     {
         Console.WriteLine( "Sub" );
     }
}
 
/// <summary>
/// 子类继承父类后,子类可以再次继承父类已经继承的接口
/// </summary>
class  Sub2 : Base, IShowMessage
{
     // 类Sub2继承IShowMessage的Show方法,
     // 与Base中继承IShowMessage的Show无关
     public  new  void  Show()
     {
         Console.WriteLine( "Sub2" );
     }

2. 显式接口

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
class  Base : IShowMessage
{
     public  void  Show()
     {
         Console.WriteLine( "Base" );
     }
     
     void  IShowMessage.Show()
     {
         Console.WriteLine( "IShowMessage" );
     }
}

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

1
2
3
Base b = new  Base();
b.Show();
((IShowMessage)b).Show();

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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. 通过显式接口来增强类型安全性,从而减少装箱操作,提高性能

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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)可以看出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
.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方法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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代码,已经没有了之前的装箱操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.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>接口相比,后者提供了类型安全检查。

1
2
3
4
5
6
7
8
int  x = 1;
IComparable c1 = x;
// 编译通过,但是运行时异常
c1.CompareTo( "2" );
 
IComparable<Int32> c2 = x;
// 编译不通过,类型不匹配
c2.CompareTo( "2" );

2. 减少装箱次数

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

1
2
3
4
5
6
7
8
9
10
int  x = 1, y = 2;
// 此处装箱一次 (x装箱)
IComparable c1 = x;
// 此处装箱一次 (y装箱)
c1.CompareTo(y);
 
// 此处装箱一次 (x装箱)
IComparable<Int32> c2 = x;
// 此处不用装箱,因为类型参数T为Int32
c2.CompareTo(y);

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class  MyClass : IComparable<Int32>,IComparable< string >
{
     #region IComparable<int> Members
 
     public  int  CompareTo( int  other)
     {
         throw  new  NotImplementedException();
     }
 
     #endregion
 
     #region IComparable<string> Members
 
     public  int  CompareTo( string  other)
     {
         throw  new  NotImplementedException();
     }
 
     #endregion
}

4. 接口和基类

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

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
{
}
标签:  CLR via C#笔记


本文转自wang_yb博客园博客,原文链接:http://www.cnblogs.com/wang_yb/archive/2011/07/28/2119737.html,如需转载请自行联系原作者
目录
相关文章
|
6月前
|
达摩院 Linux API
阿里达摩院MindOpt求解器V1.1新增C#接口
阿里达摩院MindOpt求解器发布最新版本V1.1,增加了C#相关API和文档。优化求解器产品是求解优化问题的专业计算软件,可广泛各个行业。阿里达摩院从2019年投入自研MindOpt优化求解器,截止目前经历27个版本的迭代,取得了多项国内和国际第一的成绩。就在上个月,2023年12月,在工信部产业发展促进中心等单位主办的首届能源电子产业创新大赛上,MindOpt获得电力用国产求解器第一名。本文将为C#开发者讲述如何下载安装MindOpt和C#案例源代码。
207 3
阿里达摩院MindOpt求解器V1.1新增C#接口
|
6月前
|
IDE C# 开发工具
C#系列之接口介绍
C#系列之接口介绍
|
6月前
|
编译器 C# 开发者
C# 11.0中的新特性:覆盖默认接口方法
C# 11.0进一步增强了接口的灵活性,引入了覆盖默认接口方法的能力。这一新特性允许类在实现接口时,不仅可以提供接口中未实现的方法的具体实现,还可以覆盖接口中定义的默认方法实现。本文将详细介绍C# 11.0中接口默认方法覆盖的工作原理、使用场景及其对现有代码的影响,帮助开发者更好地理解和应用这一新功能。
|
6月前
|
C# Python
C# 笔记1 - 操作目录
C# 笔记1 - 操作目录
54 0
|
6月前
|
安全 C# 开发者
C#中的默认接口方法:接口演化的新篇章
【1月更文挑战第11天】本文探讨了C# 8.0中引入的默认接口方法,这一特性允许在接口中定义具有默认实现的方法。文章介绍了默认接口方法的语法、使用场景,以及它们如何影响接口的设计和实现,同时讨论了默认接口方法带来的好处和潜在的陷阱。
|
1月前
|
C#
C# 接口(Interface)
接口定义了所有类继承接口时应遵循的语法合同。接口定义了语法合同 "是什么" 部分,派生类定义了语法合同 "怎么做" 部分。 接口定义了属性、方法和事件,这些都是接口的成员。接口只包含了成员的声明。成员的定义是派生类的责任。接口提供了派生类应遵循的标准结构。 接口使得实现接口的类或结构在形式上保持一致。 抽象类在某种程度上与接口类似,但是,它们大多只是用在当只有少数方法由基类声明由派生类实现时。 接口本身并不实现任何功能,它只是和声明实现该接口的对象订立一个必须实现哪些行为的契约。 抽象类不能直接实例化,但允许派生出具体的,具有实际功能的类。
45 9
|
29天前
|
算法 安全 测试技术
C#——刘铁猛笔记
C#——刘铁猛笔记
46 0
|
2月前
|
C# 索引
C# 一分钟浅谈:接口与抽象类的区别及使用
【9月更文挑战第2天】本文详细对比了面向对象编程中接口与抽象类的概念及区别。接口定义了行为规范,强制实现类提供具体实现;抽象类则既能定义抽象方法也能提供具体实现。文章通过具体示例介绍了如何使用接口和抽象类,并探讨了其实现方式、继承限制及实例化差异。最后总结了选择接口或抽象类应基于具体设计需求。掌握这两者有助于编写高质量的面向对象程序。
112 5
|
3月前
|
API C# 数据库
SemanticKernel/C#:实现接口,接入本地嵌入模型
SemanticKernel/C#:实现接口,接入本地嵌入模型
80 1
|
3月前
|
C#
C# 面向对象编程(三)——接口/枚举类型/泛型
C# 面向对象编程(三)——接口/枚举类型/泛型
31 0
下一篇
无影云桌面