接口是把公共实例(非静态)的方法和属性结合起来,以封装特定功能的一个集合,一旦定义了接口,就可以在类中使用实现接口中的所有成员,接口可以看作创建者和使用者之间的契约,一旦实现了接口,就不要轻易变动(如果需要变更接口,一般需要继承旧接口并且添加版本号)。我们知道在C++里面是有纯虚函数,虚继承和多重继承的,C#里面为了简化C++的这些复杂的设施,引出了接口这个概念。
 
C#接口和类的区别:

1. 不允许使用访问修饰符(public, private, protected,或者internal)修饰接口成员,所有的接口成员都是公共的。
2. 接口成员不能包含代码体
3. 接口不能定义字段成员
4. 不能用关键字static,virtual,abstract或者sealed来定义接口成员
5. 类型定义成员是禁止的。
 
 
实现C#隐式接口:

  其实接口和C++中那种头文件声明一个接口然后在cpp里面实现一遍那种做法看上去没有什么区别,只是C#把这个做的更纯粹(自从学了C#我越发觉得C++真是一门很啰嗦的语言)。如果一个类继承了一个接口,那么其对接口内的内容的实现可以在当前类实现,也可以在当前类的基类实现:
public class FuckBase
{
public void FuckSomething(int fuck)
{ }
} public class Fuck :FuckBase, A
{
public int AInt
{
get;
private set;
} public void DoSomething()
{ }
}
  比如上面这个例子,就在基类中实现了接口,如果要隐藏基类的接口,可以直接new一下。
  当然了,接口是可以继承的,比如:
public interface A
{
void DoSomething();
} public interface DeriveedA: A
{
new void DoSomething();
}
  
  在C#的接口中可以定义属性,比如:
public interface DeriveedA: A
{
new void DoSomething();
int AInt { get; set; }
}
  
  这样定义了以后,继承了DeriveedA的类必须实现AInt的get和set两个属性访问器了,并且都必须是public,有时候我们需要降低写访问器的访问权限,我们可以不在接口中定义set属性访问器,这样我们可以在类中实现有特殊访问属性的set属性访问器了,比如:
public interface DeriveedA: A
{
new void DoSomething();
int AInt { get; }
} public class Fuck : DeriveedA
{
public int AInt
{
get;
private set;//当然了这里也可以是protected
} public void DoSomething()
{ }
}
 
实现C#显式接口:

       上面的实现都属于C#的接口的隐式实现,那显式实现是什么东西?看下面的例子:
public class Starter
{
/// <summary>
/// 程序入口点
/// </summary>
/// <param name="args"></param>
public static void Main(string[] args)
{
Fuck test = new Fuck();
}
} public interface IFuck
{
void Haha();
} public class Fuck :IFuck
{
void IFuck.Haha()
{ }
}
 
  这个时候如果我们直接使用test对象,是无法调用Haha这个方法的,因为如果一个类显示实现了一个接口,那么这个接口函数将是private的,外部无法直接调用这个函数,除非把类显式转换为接口:
public class Starter
{
/// <summary>
/// 程序入口点
/// </summary>
/// <param name="args"></param>
public static void Main(string[] args)
{
Fuck test = new Fuck();
IFuck interfaceFuck = (IFuck)test;
interfaceFuck.Haha();//这个时候相当于可以使用test.Haha这个方法了
}
} public interface IFuck
{
void Haha();
} public class Fuck :IFuck
{
void IFuck.Haha()//注意显式实现接口不能带访问修饰符
{ }
}
  
  可能有人问为什么不把接口方法的实现定义为private,这个在C#里面是不允许的,如果一个类实现了一个接口(隐式实现),那么这个接口不能是private或者是protected的,必须是public的(必须是公共接口),这其实理解起来很自然,因为往往我们把一个类继承一个接口,就是需要这个接口所声明的方法。
  那么为什么还需要显式实现呢?从上面的例子我们可以看到,如果显式实现了一个接口,那么直接通过类来访问接口的方法是不行的,必须是显式转换为接口才可以访问,这就相当于了一个private的功能,比如我继承了一个IList<T>,我可能只需要IList<T>接口的一部分,那么我可以把我需要的部分隐式实现,不需要的部分显式实现,那么这个时候我既可以隐藏我不需要用到的方法,也可以把它当成一个IList<T>来用(用的时候转换为接口就好了)。这在软件工程里面是很常见的,有些时候我们写了一个类让框架来绑定,但是我们不想有些接口被误用,但是又想我们可以把它当做实现了这个接口的类型来用的时候,这样的做法就是最好的。非常符合面向对象的思想。
 
  再举几个可以用显示接口的例子。
  比如现在我有一个航空公司,公司里面有很多航班,但是其中B航班和C航班是特价航班,换句话说,就是航班之间的价格定价是不一样的,我们先假定一下所有航班的差别就是价格。
  那么我们很容易想到我们可以实现一个这样的航班类,但是我们如果要查询价格的时候,当我们显示查询B,C航班价格时,使用其各自特殊的计算方法,其他航班则选择统一的方法,在C#里面我们可以这样实现:
public class Starter
{
/// <summary>
/// 程序入口点
/// </summary>
/// <param name="args"></param>
public static void Main(string[] args)
{
Flys fly = new Flys();
IFlyB flyB = fly;
flyB.Cost();//计算航班B的价格 IFlyC flyC = fly;
flyC.Cost();//计算航班C的价格 fly.Cost();//计算普通航班的价格 Console.ReadKey();
}
}
public interface IFlyB
{
void Cost();
} public interface IFlyC
{
void Cost();
} public class Flys :IFlyB,IFlyC
{
public void Cost()
{
Console.WriteLine("Other fly");
} void IFlyB.Cost()
{
Console.WriteLine("Fly B");
} void IFlyC.Cost()
{
Console.WriteLine("Fly C");
}
}

  
  当然了,如果看过Effective C++的人已经知道我说的就是这本书上的例子,那么在C++的实现方法可以是实现一个Flys的虚基类,把Cost设为虚函数,然后派生出FlyC和FlyB,重写Cost,其他航班就显式使用虚函数的默认方法:
class Flys
{
public:
virtual void cost()const =
{
std::cout << "Other fly" << std::endl;
}
}; class FlyB :public Flys
{
public:
void cost()const override
{
std::cout << "FlyB" << std::endl;
}
}; class FlyC :public Flys
{
public:
void cost()const override
{
std::cout << "FlyC" << std::endl;
}
}; class OtherFly :public Flys
{
public:
void cost()const override
{
Flys::cost();
}
};
  
  这是一个C++的Best practice,因为这样写以后我们每次定义一种Fly都必须提供Fly的定义,可以使用默认定义,减少了程序员犯错的可能,C++可以这样写是因为C++的纯虚函数是一个很奇葩的东西,本身它应该是类似于C#的interface才对,但是却有了默认行为。
 
  第二个例子是,当我有两个接口,但是在一个在一个接口里面声明了名为Item的属性,另一个接口声明了Item的一个方法,如果我一个类要同时继承这两个接口,怎么办呢?
public interface IOne
{
int Item { get; set; }
} public interface ITwo
{
int Item();
} public class Hey : IOne, ITwo
{
public int Item { get; set;} public int Item()
{
throw new NotImplementedException();
}
}
  
  当你写出上面的代码的时候,编译器会报错,Item具有二义性,那么这个时候你必须显式实现一个接口:
public interface IOne
{
int Item { get; set; }
} public interface ITwo
{
int Item();
} public class Hey : IOne, ITwo
{
public int Item { get; set;} int ITwo.Item()
{ }
}
  
  第三种情况就是刚才说的,当有些接口你不想让别人使用,但是你却想定义自己的版本的时候,比如你想实现一个List,并且想获得一个当Remove的时候还可以获取到节点的一个方法,但是IList<T>接口并没有声明这个方法,于是你可以这样写:
public class ListNode<T> : IList<T>
{
public T RemoveAt(int index)
{ } void IList<T>.RemoveAt(int index)
{ }
}
  
  这样就实现了我们的想法,而且接口还不容易被误用。甚至你还可以在显式实现的接口void IList<T>.RemoveAt(int index)里面抛出异常,说明不支持这种方法。
 
 
 

C# Interface的使用方法探讨的更多相关文章

  1. as3.0 interface接口使用方法

    [转]as3.0 interface接口使用方法 AS在2.0的时候就支持接口了 接口能够让你的程序更具扩展性和灵活性,打个例如 比方你定义了一个方法 代码: public function aMet ...

  2. 关于Oracle本地连接出现与监听有关的问题的解决方法探讨

    关于Oracle本地连接出现与监听有关的问题的解决方法探讨 监听的作用: 用于应用桌面即用户与数据库服务器建立连接的媒介,客户端发送连接请求,监听识别请求并建立客户端与服务器的连接后,监听的使命并完成 ...

  3. GO学习-(38) Go语言结构体转map[string]interface{}的若干方法

    结构体转map[string]interface{}的若干方法 本文介绍了Go语言中将结构体转成map[string]interface{}时你需要了解的"坑",也有你需要知道的若 ...

  4. 关于java8 interface的default方法

    转自鸟窝 博主写的挺详细,不了解的看一看啊 以前经常谈论的Java对比c++的一个优势是Java中没有多继承的问题. 因为Java中子类只能继承(extends)单个父类, 尽管可以实现(implem ...

  5. WebGIS中前端JS生成等值面方法探讨

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 在之前的博文<WebGIS中等值面展示的相关方案简析&g ...

  6. 可前端解密的加密方法探讨和str_replace和preg_replace分析

    目的: 对字符串‘123456’进行后端加密,前端js可解密出真实字符 测试代码php: static $hashMap = array( '0' => '4', '1' => '9', ...

  7. Android平台Camera实时滤镜实现方法探讨(三)--通过Shader实现YUV转换RBG

    http://blog.csdn.net/oshunz/article/details/50055057 文章例如该链接通过将YUV分成三个纹理,在shader中取出并且经过公式变换,转换成RGB.我 ...

  8. Android平台Camera实时滤镜实现方法探讨(九)--磨皮算法探讨(一)

    上一篇开头提到了一些可用于磨皮的去噪算法.以下我们实现这些算法而且观察效果,咱不考虑实时性的问题 本文首先探讨的首先是<基于局部均方差相关信息的图像去噪及其在实时磨皮美容算法中的应用> 该 ...

  9. js 中读取JSON的方法探讨

    方法一:函数构造定义法返回 var strJSON = "{name:'json name'}";  //得到的JSONvar obj = new Function("r ...

随机推荐

  1. Python Pandas分组聚合

    Pycharm 鼠标移动到函数上,CTRL+Q可以快速查看文档,CTR+P可以看基本的参数. apply(),applymap()和map() apply()和applymap()是DataFrame ...

  2. NSRunLoop的进一步理解

    iPhone应用开发中关于NSRunLoop的概述是本文要介绍的内容,NSRunLoop是一种更加高明的消息处理模式,他就高明在对消息处理过程进行了更好的抽象和封装,这样才能是的你不用处理一些很琐碎很 ...

  3. BZOJ 4726: [POI2017]Sabota?

    4726: [POI2017]Sabota? Time Limit: 20 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 301  Solved ...

  4. JS this指向

    正常模式 在正常模式下独立函数的的 this 指向 undefined 或 window. <script type="text/javascript"> functi ...

  5. dwarf tower

    dwarf tower(dwarf.cpp/c/pas)[问题描述]Vasya在玩一个叫做"Dwarf Tower"的游戏,这个游戏中有n个不同的物品,它们的编号为1到n.现在Va ...

  6. 修改pip更新源

    修改pip更新源 pip安装时默认访问pypi的,但是pypi的速度对于国内来说有点慢,还在国内也有一些pip的镜像源,造福广大程序员 pipy国内镜像目前有: http://pypi.douban. ...

  7. 为你的网站或App提供免费Https支持

    网站或App Http传输是明文传输,在传输登录或支付相关的数据时,完全是裸奔. 购买证书虽然不是很贵, 但对于个人或初创团队来说,完全可以申请免费的证书来提供Https访问. 本文介绍通过start ...

  8. ionic 图标以及启动页图片不能正确加载

    前段时间莫名其妙的发现发布的app不能正常的现实图标和启动页了,加载出来的图标以及图片显示的都是cordova的默认图片以及启动页图片 在网上找了很多教程各种查找都不能解决方法 表现原因为: 项目根目 ...

  9. docker run elasticsearch

    docker run -d --name=esNode1 -p 9200:9200 -p 9300:9300 elasticsearch:2.3 -Des.network.publish_host=& ...

  10. vim(vi)常用操作及记忆方法

    vi(vim)可以说是linux中用得最多的工具了,不管你配置服务也好,写脚本也好,总会用到它.但是,vim作为一个“纯字符”模式下的工具,它的操作和WINDOWS中的文本编辑工具相比多少有些复杂.这 ...