这一篇主要来解析关于面向对象中最总要的一个概念——接口。

对于接口来说,C#是有规定使用Interface关键字来声明接口。它的声明是和类一致的。可以说接口就是一个特殊的抽象类。如下代码:

class Program
{
static void Main(string[] args)
{
}
} //声明一个可以飞的接口
interface IRunable
{
//包含可以被继承的子类实现的方法
void Run();
}

由以前的抽象类的知识可以知道,抽象类是没有办法实例化的(因为含有抽象成员,而抽象成员不含有方法体)。那么接口可不可以实例化呢?答案是肯定的,不能实例化。看下面的一段代码:

这个时候编译器告诉我们无法创建抽象类或者接口的实例。

二,接口可以定义哪些成员

1)接口就是一个定义“具有某种能力的抽象类”,既然接口是类,那么它的内部可以定义哪些成员呢?

首先,在普通的类中,可以有字段,属性,方法,索引器,抽象方法等等。那么接口呢?

看下面直接声明字段,编译器会报错,告诉我们接口内不能声明字段

既然接口内不能有字段,那也就不存在封装字段了。所以上边图示的封装字段的代码也是错误的。

同理由上面的代码也可以知道,在接口中是不可以定义显式的属性(因为在属性中要操作字段赋值,但是字段没有办法在接口中声明)。

那么接口可以声明自动属性么?看下面的代码:

 //声明一个可以飞的接口
interface IRunable
{
//声明字段
int nAge { get; set; } string strName { get; set; }
////包含可以被继承的子类实现的方法
void Run(); }

代码可以顺利编译通过,那么是为什么呢?这就要看.NET的源码,我把源码编译后的比较结果如下图:

抽象方法就不用多了,本来接口就是一个抽象爱类,当然可以定义抽象类,但是不在使用abstract关键字,而且方法必须没有方法体;

2)继承接口的子类必须实现接口的所有抽象成员。

我们先来看下面的代码:

 //声明一个接口,其中包含属性和未实现方法void
interface IRunable
{
string strName { get; set; }
void Run();
}

下面来一个实现类,如下:

 class Person:IRunable
{
public void Run()
{
Console.WriteLine("我可以奔跑!");
}
}

这时候,我们编译,编译器会告诉我们什么呢?如下图:

所以继承接口的类,必须实现接口的所有抽象成员。

正确的代码如下:

 class Person:IRunable
{
public void Run()
{
Console.WriteLine("我可以奔跑!");
} public string strName
{
get
{
return strName;
}
set
{
strName = value;
}
}
}

通过以上的代码可以发现:

①我们的继承类在实现接口成员的时候不需要使用override关键字

②实现接口的时候,必须保持签名一致

由前面抽象类的知识我们有没有这样的疑问,什么时候使用抽象类,什么时候使用接口呢?

总结如下:

①使用抽象类:可以找到父类,并且希望通过父类继承给子类一些成员

②使用接口:接口就是一个纯粹的为了规范实现的类。比如:多个类具有相同的方法,但是却找不到父类,就可以将方法定义在接口中。让这些类去实现。

下面纠纷别来看两端代码,比较抽象类和接口的异同,首先是抽象类:

class Program
{
static void Main(string[] args)
{
Student s = new Student();
//Student类通过继承获得NAge属性
s.NAge = 10;
s.Eat(); Console.WriteLine("--------Student和Worker类分别通过继承获得了父类的非私有成员,实现了父类的抽象方法--------"); Worker w = new Worker();
//Worker类通过继承获得NAge属性
w.NAge = 40;
w.Eat(); Console.ReadKey();
}
} //定义父类
abstract class Person
{
private int nAge; public int NAge
{
get { return nAge; }
set { nAge = value; }
} private void Run()
{
Console.WriteLine("我是父类,我可以跑!");
}
public abstract void Eat(); } class Student : Person
{
//子类覆写了父类的抽象方法
public override void Eat()
{
Console.WriteLine("我是子类,我继承了父类,我可以在学校吃饭!");
}
} class Worker:Person
{
//同样Worker也通过继承获得了父类的非私有成员
public override void Eat()
{
Console.WriteLine("我是子类,我继承父类,我可以在工厂吃饭");
}
}

接下来,来看看接口是怎么规范多个类的实现的。

class Program
{
static void Main(string[] args)
{
Student s = new Student();
s.strName = "小学生";
s.Run();
Console.WriteLine(s.strName);
Console.WriteLine("--------------------");
Worker w = new Worker();
w.strName = "看我能不能渎职";
w.Run();
Console.WriteLine(w.strName); Console.ReadKey();
}
} interface IRunable
{
//规范子类必须实现strName属性
string strName { get; set; }
//规范子类必须实现Run()方法
void Run(); } class Student:IRunable
{
//这里是子类的字段
string strname;
public string strName
{
get
{
return strname;
}
set
{
strname = value;
}
} public void Run()
{
Console.WriteLine("我是小学生,我在学校里面跑步!");
} } class Worker:IRunable
{
string strname;
public string strName
{
get
{
return "工人";
}
set
{
strname = value;
}
} public void Run()
{
Console.WriteLine( "我是工人,我需要在厂区跑!");
}
}

由以上的代码可不可以发现,接口仅仅在规定一个规范子类的实现,而抽象类可以通过继承,继承给子类某些成员。

最后来看一下,接口的显示实现,我先看接口的普通实现(以上的代码实现接口的方式都是隐式实现)

interface IRunable
{
//规范子类必须实现strName属性
string strName { get; set; }
//规范子类必须实现Run()方法
void Run(); } class Student:IRunable
{
//这里是子类的字段
string strname;
public string strName
{
get
{
return strname;
}
set
{
strname = value;
}
} public void Run()
{
Console.WriteLine("我是小学生,我在学校里面跑步!");
} }

显式实现接口

 class Student:IRunable
{
//这里是子类的字段
string strname;
//显示实现接口
string IRunable.strName
{
get
{
return strname;
}
set
{
strname = value;
}
} void IRunable.Run()
{
Console.WriteLine("我是小学生,我在学校里面跑步!");
} }

显示的实现接口是为了解决方法名冲突的问题。但是显示实现接口会出现,在上面的代码中会出现一个问题,如下图:

为什么会这样呢?

因为显式实现接口的方法是私有的,不能通过对象变量来调用。那应该怎么调用呢,看下面的代码:

class Program
{
static void Main(string[] args)
{ //里氏替换原则,父类变量指向子类对象,并通过父类变量调用子类方法
IRunable ir = new Student();
ir.Run();
Console.ReadKey();
}
} interface IRunable
{
//规范子类必须实现strName属性
string strName { get; set; }
//规范子类必须实现Run()方法
void Run(); } class Student:IRunable
{
//这里是子类的字段
string strname;
//显示实现接口
string IRunable.strName
{
get
{
return strname;
}
set
{
strname = value;
}
} void IRunable.Run()
{
Console.WriteLine("我是小学生,我在学校里面跑步!");
} // Student s = new Student(); }

打印结果如下:

显式实现接口,这个接口的方法,只能通过接口变量来调用。

接口导图总结如下:


毕业实习交流群:221376964。你也可以关注我的新浪微博进行交流。

版权声明:本文为博主原创文章,未经博主允许不得转载。

【C#小知识】C#中一些易混淆概念总结(八)---------解析接口 分类: C# 2014-02-18 00:09 2336人阅读 评论(4) 收藏的更多相关文章

  1. C/C++中const的用法 分类: C/C++ 2015-07-05 00:43 85人阅读 评论(0) 收藏

    const是C语言的关键字,经C++进行扩充,变得功能强大,用法复杂.const用于定义一个常变量(只读变量),当const与指针,引用,函数等结合起来使用时,情况会变得复杂的多.下面将从五个方面总结 ...

  2. 【C#小知识】C#中一些易混淆概念总结(四)---------解析Console.WriteLine() 分类: C# 2014-02-05 17:18 1060人阅读 评论(0) 收藏

    目录: [C#小知识]C#中一些易混淆概念总结 [C#小知识]C#中一些易混淆概念总结(二) [C#小知识]C#中一些易混淆概念总结(三) ------------------------------ ...

  3. iOS中UITextField 使用全面解析 分类: ios技术 2015-04-10 14:37 153人阅读 评论(0) 收藏

    //初始化textfield并设置位置及大小   UITextField *text = [[UITextField alloc]initWithFrame:CGRectMake(20, 20, 13 ...

  4. 解决ORA-29857:表空间中存在域索引和/或次级对象 & ORA-01940:无法删除当前连接的用户问题 分类: oracle sde 2015-07-30 20:13 8人阅读 评论(0) 收藏

    今天ArcGIS的SDE发生了一点小故障,导致系统表丢失,所以需要重建一下SDE数据库,在删除SDE用户和所在的表空间过程中遇到下面两个ORA错误,解决方法如下: 1)删除表空间时报错:ORA-298 ...

  5. VS2010中使用命令行参数 分类: c/c++ 2014-07-11 22:24 634人阅读 评论(0) 收藏

    在Linux下编程习惯了使用命令行参数,故使用VS2010时也尝试了一下. 新建项目,c++编写程序如下: #include<iostream> #include<fstream&g ...

  6. 认识C++中的临时对象temporary object 分类: C/C++ 2015-05-11 23:20 137人阅读 评论(0) 收藏

    C++中临时对象又称无名对象.临时对象主要出现在如下场景. 1.建立一个没有命名的非堆(non-heap)对象,也就是无名对象时,会产生临时对象. Integer inte= Integer(5); ...

  7. Java获取项目中的路径 分类: Java Game 2014-08-14 10:17 122人阅读 评论(0) 收藏

    在项目中经常需要获取某个文件的路径: 在这里提供一些获取路径的方法.. 1.此种方式获取的路径,是当前类所在的路径: UserDAOTest.class.getResource("UserD ...

  8. C#中的线程(上)-入门 分类: C# 线程 2015-03-09 10:56 53人阅读 评论(0) 收藏

    1.     概述与概念 C#支持通过多线程并行地执行代码,一个线程有它独立的执行路径,能够与其它的线程同时地运行.一个C#程序开始于一个单线程,这个单线程是被CLR和操作系统(也称为"主线 ...

  9. windows server 2008 R2域中的DC部署 分类: AD域 Windows服务 2015-06-06 21:09 68人阅读 评论(0) 收藏

    整个晚上脑子都有点呆滞,想起申请注册好的博客还从来都不曾打理,上来添添生机.从哪里讲起呢,去年有那么一段时间整个人就陷在域里拔不出来,于是整理了一些文档,害怕自己糊里糊涂的脑子将这些东西会在一觉醒来全 ...

随机推荐

  1. Content-Type,内容类型

    Content-Type,内容类型,一般是指网页中存在的Content-Type,ContentType属性指定请求和响应的HTTP内容类型.如果未指定 ContentType,默认为text/htm ...

  2. 用C#开发的双色球走势图(原创)值得园友拥有(二)接上一篇

    昨晚由于时间的原因只写了一部分内容,今天将这一部分内容补充完毕,多谢各位园友的支持. 这是用C#开发的双色球走势图(原创)值得园友拥有 新的园友可以看昨晚写的内容,以免脱节.首先回复园友的评论,有说好 ...

  3. Android 实现界面(Activity)的跳转

    界面跳转 如,我想重一个界面A跳转到界面B,可以用,setContentView(R.layout.activity_login); 但是他其实只是将改界面铺在了最顶层,而按menu这些菜单其实还是底 ...

  4. JavaScript中使用function作为对象键值

    JavaScript的键值只能是string或者number,这一点真是返祖现象啊.现在我面临的问题: var funcs = {}; var funcA = function() { }; var ...

  5. 排序算法之快速排序(Quicksort)解析

    一.快速排序算法的优点,为什么称之为快排? Quicksort是对归并排序算法的优化,继承了归并排序的优点,同样应用了分治思想. 所谓的分治思想就是对一个问题“分而治之”,用分治思想来解决问题需要两个 ...

  6. ie下警告console未定义

    低版本IE6/7/8/9浏览器没有定义console对象,所以代码会中断执行.自己测试,ie11也没有(打开控制台的情况下可以用) 可以用如下代码完美解决. window.console = wind ...

  7. .net core 基于Jwt实现Token令牌

    Startup类ConfigureServices中 services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJw ...

  8. requirejs的插件介绍与制作

    本文由作者郑海波授权网易云社区发布. 前言 我这里就不介绍requirejs了, 简而言之: requirejs是支持AMD规范的模块加载器, 事实上它也是AMD的最直接推动者. 现在可供挑选的开源模 ...

  9. Android Source 源码已下载但 Android Studio 找不到的解决办法

    Android Studio 2.1 reporting in: solved the issue by resetting SDK. Preferences -> Appearance &am ...

  10. Datetimepicker.js用法

    $('.form_date').datetimepicker({//初始化 language: 'zh-CN', //weekStart: 1, //todayBtn: 1, autoclose: 1 ...