接口

什么是接口

接口是指定一组函数成员而不实现它们的引用类型。所以只能类和结构来实现接口。

这种描述比较抽象,直接来看个示例。

下例中,Main方法创建并初始化了一个CA类的对象,并将该对象传递给PrintInfo方法。

classCA

{public stringName;public intAge;

}classCB

{public stringFirst;public stringLast;public doublePersonsAge;

}classProgram

{static voidPrintInfo(CA item)

{

Console.WriteLine("Name: {0},Age {1}",item.Name,item.Age);

}static voidMain()

{

CA a=new CA(){Name="John Doe",Age=35};

PrintInfo(a);

}

}

只要传入的是CA类型的对象,PrintInfo就能正常工作。但如果传入的是CB,就不行了。

现在的代码不能满足上面的需求,原因有很多。

PrintInfo的形参指明了实参必须为CA类型的对象

CB的结构与CA不同,字段的名称和类型与CA不一样

接口解决了这一问题。

声明一个IInfo接口,包含两个方法–GetName和GetAge,每个方法都返回string

类CA和CB各自实现了IInfo接口,并实现了两个方法

Main创建了CA和CB的实例,并传入PrintInfo

由于类实例实现了接口,PrintInfo可以调用那两个方法,每个类实例执行各自的方法,就好像是执行自己类声明中的方法

interface IInfo //声明接口

{stringGetName();stringGetAge();

}class CA:IInfo //声明了实现接口的CA类

{public stringName;public intAge;public string GetName(){returnName;}public string GetAge(){returnAge.ToString();}

}class CB:IInfo //声明了实现接口的CB类

{public stringFirst;public stringLast;public doublePersonsAge;public string GetName(){return First+""+Last;}public string GetAge(){returnPersonsAge.ToString();}

}classProgram

{static voidPrintInfo(IInfo item)

{

Console.WriteLine("Name: {0},Age {1}",item.GetName(),item.GetAge());

}static voidMain()

{var a=new CA(){Name="John Doe",Age=35};var b=new CB(){First="Jane",Last="Doe",PersonsAge=33};

PrintInfo(a);

PrintInfo(b);

}

}

使用IComparable接口的示例

第一行代码创建了包含5个无序整数的数组

第二行代码使用了Array类的静态Sort方法来排序元素

用foreach循环输出它们,显式以升序排序的数字

var myInt=new[]{20,4,16,9,2};

Array.Sort(myInt);foreach(var i inmyInt)

{

Console.WriteLine("{0}",i);

}

Sort方法在int数组上工作良好,但如果我们尝试在自己的类上使用会发生什么呢?

classMyClass

{public intTheValue;

}

...

MyClass[] mc=new MyClass[5];

...

Array.Sort(mc);

运行上面的代码,将会得到一个异常。Sort并不能对MyClass对象数组排序,因为它不知道如何比较自定义的对象。Array类的Sort方法其实依赖于一个叫做IComparable的接口,它声明在BCL中,包含唯一的方法CompareTo。

public interfaceIComparable

{int CompareTo(objectobj);

}

尽管接口声明中没有为CompareTo方法提供实现,但IComparable接口的.NET文档中描述了该方法应该做的事情,可以在创建实现该接口的类或结构时参考。

文档中写到,在调用CompareTo方法时,它应该返回以下几个值:

负数值 当前对象小于参数对象

整数值 当前对象大于参数对象

零 两个对象相等

我们可以通过让类实现IComparable来使Sort方法可以用于MyClass对象。要实现一个接口,类或结构必须做两件事情:

必须在基类列表后面列出接口名称

必须实现接口的每个成员

例:MyClass中实现了IComparable接口

classMyClass:IComparable

{public intTheValue;public int CompareTo(objectobj)

{var mc=(MyClass)obj;if(this.TheValuemc.TheValue)return 1;return 0;

}

}

例:完整示例代码

classMyClass:IComparable

{public intTheValue;public int CompareTo(objectobj)

{var mc=(MyClass)obj;if(this.TheValuemc.TheValue)return 1;return 0;

}

}classProgram

{static void PrintInfo(strings,MyClass[] mc)

{

Console.WriteLine(s);foreach(var m inmc)

{

Console.WriteLine("{0}",m.TheValue);

}

Console.WriteLine("");

}static voidMain()

{var myInt=new[] {20,4,16,9,2};

MyClass[] mcArr=new MyClass[5];for(int i=0;i<5;i++)

{

mcArr[i]=newMyClass();

mcArr[i].TheValue=myInt[i];

}

PrintOut("Initial Order:",mcArr);

Array.Sort(mcArr);

PrintOut("Sorted Order:",mcArr);

}

}

声明接口

上一节使用的是BCL中已有的接口。现在我们来看看如何声明接口。

关于声明接口,需要知道的重要事项如下:

接口声明不能包含以下成员

数据成员

静态成员

接口声明只能包含如下类型的非静态成员函数的声明

方法

属性

事件

索引器

这些函数成员的声明不能包含任何实现代码,只能用分号

按照惯例,接口名称以大写字母I(Interface)开始

与类和结构一样,接口声明也可以分布

例:两个方法成员接口的声明

关键字 接口名称

↓ ↓interfaceIMyInterface1

{int DoStuff(int nVar1,long lVar2); //分号替代了主体

double DoOtherStuff(string s,longx);

}

接口的访问性和接口成员的访问性之间有一些重要区别

接口声明可以有任何的访问修饰符public、protected、internal或private

然而,接口的成员是隐式public的,不允许有任何访问修饰符,包括public

接口允许访问修饰符

↓public interfaceIMyInterface2

{private int Method1(int nVar1); //错误

接口成员不允许访问修饰符

}

实现接口

只有类和结构才能实现接口。

在基类列表中包括接口名称

实现每个接口成员

例:MyClass实现IMyInterface1接口

classMyClass:IMyInterface1

{int DoStuff(int nVar1,longlVar2)

{...}//实现代码

double DoOtherStuff(string s,longx)

{...}//实现代码

}

关于实现接口,需要了解以下重要事项:

如果类实现了接口,它必须实现接口的所有成员

如果类从基类继承并实现了接口,基类列表中的基类名称必须放在所有接口之前。

基类必须放在最前面 接口名

↓ ↓class Derived:MyBaseClass,IIfc1,IEnumerable,IComparable

简单接口示例

interfaceIIfc1

{void PrintOut(strings);

}classMyClass:IIfc1

{public void PrintOut(strings)

{

Console.WriteLine("Calling through: {0}",s);

}

}classProgram

{static voidMain()

{var mc=newMyClass();

mc.PrintOut("object");

}

}

接口是引用类型

接口不仅是类或结构要实现的成员列表。它是一个引用类型。

我们不能直接通过类对象的成员访问接口。然而,我们可以通过把类对象引用强制转换为接口类型来获取指向接口的引用。一旦有了接口引用,我们就可以使用点号来调用接口方法。

例:从类对象引用获取接口引用

IIfc1 ifc=(IIfc1)mc; //转换为接口,获取接口引用

ifc.PrintOut("interface"); //使用接口的引用调用方法

例:类和接口的引用

interfaceIIfc1

{void PrintOut(strings);

}classMyClass:IIfc1

{public void PrintOut(strings)

{

Console.WriteLine("Calling through: {0}",s);

}

}classProgram

{static voidMain()

{var mc=newMyClass();

mc.PrintOut("object"); //调用类对象的实现方法

IIfc1 ifc=(IIfc1)mc;

ifc.PrintOut("interface"); //调用引用方法

}

}

接口和as运算符

上一节,我们已经知道可以使用强制转换运算符来获取对象接口引用,另一个更好的方式是使用as运算符。

如果我们尝试将类对象引用强制转换为类未实现的接口的引用,强制转换操作会抛出异常。我们可以通过as运算符来避免该问题。

如果类实现了接口,表达式返回指向接口的引用

如果类没有实现接口,表达式返回null

ILiveBirth b=a asILiveBirth;if(b!=null)

{

Console.WriteLine("Baby is called: {0}",b.BabyCalled());

}

实现多个接口

类或结构可以实现多个接口

所有实现的接口必须列在基类列表中并以逗号分隔(如果有基类名称,则在其后)

interface IDataRetrieve{intGetData();}interface IDataStore{void SetData(intx);}classMyData:IDataRetrieve,IDataStore

{intMem1;public int GetData(){returnMem1;}public void SetData(int x){Mem1=x;}

}classProgram

{static voidMain()

{var data=newMyData();

data.SetData(5);

Console.WriteLine("Value = {0}",data.GetData());

}

}

实现具有重复成员的接口

由于接口可以多实现,有可能多个接口有相同的签名和返回类型。编译器如何处理这种情况呢?

例:IIfc1和IIfc2具有相同签名

interfaceIIfc1

{void PrintOut(strings);

}interfaceIIfc2

{void PrintOut(stringt);

}

答案是:如果一个类实现了多接口,并且其中有些接口有相同签名和返回类型,那么类可以实现单个成员来满足所有包含重复成员的接口。

例:MyClass 类实现了IIfc1和IIfc2.PrintOut满足了两个接口的需求。

classMyClass:IIfc1,IIfc2

{public void PrintOut(string s)//两个接口单一实现

{

Console.WriteLine("Calling through: {0}",s);

}

}classProgram

{static voidMain()

{var mc=newMyClass();

mc.PrintOut("object");

}

}

多个接口的引用

如果类实现了多接口,我们可以获取每个接口的独立引用。

例:下面类实现了两个具有当PrintOut方法的接口,Main中以3种方式调用了PrintOut。

通过类对象

通过指向IIfc1接口的引用

通过指向IIfc2接口的引用

interfaceIIfc1

{void PrintOut(strings);

}interfaceIIfc2

{void PrintOut(stringt);

}classMyClass:IIfc1,IIfc2

{public void PrintOut(strings)

{

Console.WriteLine("Calling through: {0}",s);

}

}classProgram

{static voidMain()

{var mc=newMyClass();

IIfc1 ifc1=(IIfc1)mc;

IIfc2 ifc2=(IIfc2)mc;

mc.PrintOut("object");

ifc1.PrintOut("interface 1");

ifc2.PrintOut("interface 2");

}

}

派生成员作为实现

实现接口的类可以从它的基类继承实现的代码。

例:演示 类从基类代码继承了实现

IIfc1是一个具有PrintOut方法成员的接口

MyBaseClass包含一个PrintOut方法,它和IIfc1匹配

Derived类有空的声明主体,但它派生自MyBaseClass,并在基类列表中包含了IIfc1

即使Derived的声明主体为空,基类中的代码还是能满足实现接口方法的需求

interfaceIIfc1

{void PrintOut(strings);

}classMyBaseClass

{public void PrintOut(strings)

{

Console.WriteLine("Calling through: {0}",s);

}

}classDerived:MyBaseClass,IIfc1

{

}classProgram

{static voidMain()

{var d=newDerived();

d.PrintOut("object");

}

}

显式接口成员实现

上面,我们已经看到了单个类可以实现多个接口需要的所有成员。

但是,如果我们希望为每个接口分离实现该怎么做呢?这种情况下,我们可以创建显式接口成员实现。

与所有接口实现相似,位于实现了接口的类或结构中

它使用限定接口名称来声明,由接口名称和成员名称以及它们中间的点分隔符号构成

classMyClass:IIfc1,IIfc2

{void IIfc1.PrintOut(strings)

{...}void IIfc2.PrintOut(strings)

{...}

}

例:MyClass为两个解耦的成员声明了显式接口成员实现。

interfaceIIfc1

{void PrintOut(strings);

}interfaceIIfc2

{void PrintOut(stringt);

}classMyClass:IIfc1,IIfc2

{void IIfc1.PrintOut(strings)

{

Console.WriteLine("IIfc1: {0}",s);

}void IIfc2.PrintOut(strings)

{

Console.WriteLine("IIfc2: {0}",s);

}

}classProgram

{static voidMain()

{var mc=newMyClass();

IIfc1 ifc1=(IIfc1)mc;

ifc1.PrintOut("interface 1");

IIfc2 ifc2=(IIfc2)mc;

ifc2.PrintOut("interface 2");

}

}

如果有显式接口成员实现,类级别的实现是允许的,但不是必需的。显式实现满足了类或结构必须实现的方法。因此,我们可以有如下3种实现场景。

类级别实现

显式接口成员实现

类级别和显式接口成员实现

访问显式接口成员实现

显式接口成员实现只可以通过指向接口的引用来访问。即其他的类成员都不可以直接访问它们。

例:如下MyClass显式实现了IIfc1接口。注意,即使是MyClass的另一成员Method1,也不可以直接访问显式实现。

Method1的前两行编译错误,因为方法在尝试直接访问实现

只有Method1的最后一行代码才可以编译,因此它强制转换当前对象的引用(this)为接口类型的引用,并使用这个指向接口的引用来调用显式接口实现

classMyClass:IIfc1

{void IIfc1.PrintOut(strings)

{

Console.WriteLine("IIfc1");

}public voidMethod1()

{

PrintOut("..."); //编译错误

this.PrintOut("..."); //编译错误

((IIfc1)this).PrintOut("...");

转换为接口引用

}

}

这个限制对继承产生了重要影响。由于其他类成员不能直接访问显式接口成员实现,衍生类的成员也不能直接访问它们。它们必须总是通过接口的引用来访问。

接口可以继承接口

之前我们已经知道接口实现可以从基类继承,而接口本身也可以从一个或多个接口继承。

要指定某个接口继承其他的接口,应在接口声明中把某接口以逗号分隔的列表形式放在接口名称的冒号之后

与类不同,它在基类列表中只能有一个类名,接口可以在基接口列表中有任意多个接口

列表中的接口本身可以继承其他接口

结果接口包含它声明的所有接口和所有基接口的成员

interfaceIDataIO:IDataRetrieve,IDataStore

{

...

}

例:IDataIO接口继承了两个接口

interfaceIDataRetrieve

{intGetData();

}interfaceIDataStore

{void SetData(intx);

}interfaceIDaTaIO:IDataRetrieve,IDataStore

{

}classMyData:IDataIO

{intnPrivateData;public intGetData()

{returnnPrivateData;

}public void SetData(intx)

{

nPrivateData=x;

}

}classProgram

{static voidMain()

{var data=newMyData();

data.SetData(5);

Console.WriteLine("{0}",data.GetData());

}

}

不同类实现一个接口的示例

interface ILiveBirth //声明接口

{stringBabyCalled();

}class Animal{} //基类Animal

class Cat:Animal,ILiveBirth //声明Cat类

{stringILiveBirth.BabyCalled()

{return "kitten";

}

}class Dog:Animal,ILiveBirth //声明DOg类

{stringILiveBirth.BabyCalled()

{return "puppy";

}

}class Bird:Animal //声明Bird类

{

}classProgram

{static voidMain()

{

Animal[] animalArray=new Animal[3];

animalArray[0]=newCat();

animalArray[1]=newBird();

animalArray[2]=newDog();foreach(Animal a inanimalArray)

{

ILiveBirth b= a as ILiveBirth;//如果实现ILiveBirth

if(b!=null)

{

Console.WriteLine("Baby is called: {0}",b.BabyCalled());

}

}

}

}

c#中接口的使用方法图解_C#图解教程 第十五章 接口相关推荐

  1. C#图解教程 第二十五章 其他主题

    其他主题 概述 在本章中,我会介绍使用C#时的一些重要而又不适合放到其他章节的主题,包括字符串操作.可空类型.Main方法.文档注释以及嵌套类型. 字符串 对于内部计算来说0和1很适合,但是对于人类可 ...

  2. Visual C++ 2008入门经典 第十五章 在窗口中绘图

    /*第十五章 在窗口中绘图 主要内容: 1 Windows为窗口绘图提供的坐标系统 2 设置环境及其必要性 3 程序如何以及在窗口中绘图 4 如何定义鼠标消息的处理程序 5 如何定义自己的形状类 6 ...

  3. 第十五章 使用管理门户SQL接口(一)

    文章目录 第十五章 使用管理门户SQL接口(一) 管理门户SQL工具 选择命名空间 用户自定义 执行SQL查询 编写SQL语句 表拖放 执行查询选项 显示计划按钮 SQL语句的结果 查询数据显示 查询 ...

  4. SpringBoot 系列教程(八十五):Spring Boot使用MD5加盐验签Api接口之前后端分离架构设计

    加密算法参考: 浅谈常见的七种加密算法及实现 加密算法参考: 加密算法(DES,AES,RSA,MD5,SHA1,Base64)比较和项目应用 目的: 通过对API接口请求报文签名,后端进行验签处理, ...

  5. 《Reids 设计与实现》第十五章 集群(中)

    <Reids 设计与实现>第十五章 集群(中) 文章目录 <Reids 设计与实现>第十五章 集群(中) 四.在集群中执行命令 1.计算键属于哪个槽 2.判断槽是否由当前节点负 ...

  6. 服务器虚拟机挂载光驱,如何在VMware vSphere client中安装光驱的方法步骤(图文教程)...

    VMware vSphere作为服务器虚拟化领域最具影响力的软件,帮助了众多企业解决服务器工作负载等问题,而VMware vSphere Client是VMware vSphere中最重要的套件之一, ...

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

    接口(interface)和类(class)是CLR中应用最为广泛的两个概念.灵活的应用接口,可以构造出各种经典的设计模式. 接口的语法并不复杂,本篇主要记录接口中一些容易忽略的地方,以及如何更好的使 ...

  8. C#编程(二十五)----------接口

    接口 如果一个类派生自一个接口,声明这个类就会实现某些函数.并不是所有的面向对象的语言都支持接口. 例如,有一个接口:IDispoable,包含一个方法Dispose(),该方法又类实现,用于清理代码 ...

  9. JAVA学习笔记(第五章 接口与继承)

    一.什么是接口 接口就像是一种模板,接口中放着没有内容的方法,类要是使用接口就必须声明接口中的方法.下面继续以LOL游戏为例: LOL的英雄,有些能够使用物理攻击,有些能够使用魔法攻击击,有些则两种攻 ...

最新文章

  1. 详解:智能医学影像分析的前沿与挑战 | 硬创公开课
  2. 通过ArcCatalog连接ArcGIS Server的种种问题
  3. 浅析网站SEO优化对长尾关键词保持好感度的四大技巧
  4. 最聪明女生集结清华,挑战人工智能
  5. SharedPreferences基础
  6. 雷军微博“导购”小米8 SE:名副其实的小屏旗舰!
  7. java小数丢失精度_Java中的小数运算与精度损失
  8. 资源 | 忘了Python关键语句?这份备忘录拯救你的记忆
  9. uniapp小程序webSocket封装、断线重连、心跳检测
  10. PHP-Web聊天室 一天即可打造自己的聊天室-严双双-专题视频课程
  11. ps4插html屏幕不亮光,ps4连接显示器怎么老是黑屏
  12. openGauss社区成立QA SIG
  13. 想要买房的人究竟有多可悲?! --水木周平
  14. 一个超级实用的单片机调试技巧!DWT组件
  15. 为 a.out 举行一个特殊的告别仪式
  16. MySQL中出现Duplicate entry 'XXX' for key 'PRIMARY'解决方案
  17. 每日一句_《南柯子·池水凝新碧》
  18. 三个和尚没水喝的启发
  19. 欧文计算机科学排名,加州大学欧文分校计算机科学世界排名2019年最新排名第55(THE世界排名)...
  20. 怎样学习计算机语言?

热门文章

  1. ubuntu安装deepin terminal 终端
  2. Python---内置函数
  3. consul安装配置使用
  4. while循环中,break,continue,return的差别
  5. Maven 学习Tips
  6. 设计模式:状态模式(State Pattern)
  7. JSP学习笔记(七):使用JavaBean
  8. 011:视图函数介绍
  9. 04 pod setup 慢的问题
  10. python selenium error “Geckodriver executable needs to be in PATH”