【C#小知识】C#中一些易混淆概念总结(八)---------解析接口

简介:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
class  Program
     {
         static  void  Main( string [] args)
         {
         }
     }
     //声明一个可以飞的接口
     interface  IRunable
     {
         //包含可以被继承的子类实现的方法
         void  Run();
     }


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

162348023392706.png

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

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

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

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

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

170018381645758.png

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

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

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

1
2
3
4
5
6
7
8
9
10
//声明一个可以飞的接口
     interface  IRunable
     {
         //声明字段
         int  nAge {  get set ; }
                                                     
         string  strName {  get set ; }
         ////包含可以被继承的子类实现的方法
         void  Run();
     }

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

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

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

我们先来看下面的代码:


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

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

1
2
3
4
5
6
7
class  Person:IRunable
     {
        public   void  Run()
         {
             Console.WriteLine( "我可以奔跑!" );
         }
     }

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

172219423394423.png

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

正确的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class  Person:IRunable
     {
        public   void  Run()
         {
             Console.WriteLine( "我可以奔跑!" );
         }
        public  string  strName
        {
            get
            {
                return  strName;
            }
            set
            {
                strName = value;
            }
        }
     }


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

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

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

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

总结如下:

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

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

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

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
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( "我是子类,我继承父类,我可以在工厂吃饭" );
         }
     }


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

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
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(   "我是工人,我需要在厂区跑!" );
         }
     }

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

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

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  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( "我是小学生,我在学校里面跑步!" );
         }
     }


显式实现接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class  Student:IRunable
     {
         //这里是子类的字段
         string  strname;
         //显示实现接口
         string  IRunable.strName
         {
             get
             {
                 return  strname;
             }
             set
             {
                 strname = value;
             }
         }
         void  IRunable.Run()
         {
             Console.WriteLine( "我是小学生,我在学校里面跑步!" );
         }
     }


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

172336139163508.png

为什么会这样呢?

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

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
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();
                  
     }

打印结果如下:

172350330329904.png

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

接口导图总结如下:

172351588523426.png








     本文转自yisuowushinian 51CTO博客,原文链接:http://blog.51cto.com/yisuowushinian/1359968,如需转载请自行联系原作者


相关文章
|
6天前
|
存储 监控 关系型数据库
InfluxDB入门:基础概念解析
【4月更文挑战第30天】InfluxDB是开源时序数据库,擅长处理实时数据,常用于监控和分析。本文介绍了其基础概念:数据库(数据容器)、测量值(类似表)、字段(数据值)、标签(元数据)、时间戳和数据点。InfluxDB特性包括高性能写入、灵活查询(InfluxQL和Flux)、可扩展性及活跃社区支持。了解这些概念有助于更好地使用InfluxDB处理时间序列数据。
【期末不挂科-单片机考前速过系列P7】(第七章:11题速过串行口基本概念/结构/工作方式/双机通信例题)经典例题盘点(带图解析)
【期末不挂科-单片机考前速过系列P7】(第七章:11题速过串行口基本概念/结构/工作方式/双机通信例题)经典例题盘点(带图解析)
|
4天前
|
C#
C#的类和对象的概念学习案例刨析
【5月更文挑战第17天】C#是一种面向对象的语言,以类和对象为核心。类作为对象的模板,定义了属性(如Name, Age)和行为(如Greet)。对象是类的实例,可设置属性值。封装通过访问修饰符隐藏实现细节,如Customer类的私有name字段通过Name属性访问。继承允许新类(如Employee)从现有类(Person)继承并扩展。多态让不同对象(如Circle, Square)共享相同接口(Shape),实现抽象方法Area,提供灵活的代码设计。
13 1
Android-kotlin-接口与多态的表现,面经解析
Android-kotlin-接口与多态的表现,面经解析
|
6天前
|
存储 Java 程序员
Java面向对象编程的基础概念解析
Java面向对象编程的基础概念解析
17 0
|
6天前
|
存储 开发框架 .NET
C#中将DataTable转化成ListT的方法解析
C#中将DataTable转化成ListT的方法解析
9 0
|
6天前
|
XML 存储 开发框架
c#教你网站数据轻松解析抓取,HtmlAgilityPack解析的奇妙之处
c#教你网站数据轻松解析抓取,HtmlAgilityPack解析的奇妙之处
14 0
|
6天前
|
Java Spring
Javaweb之SpringBootWeb案例之AOP核心概念的详细解析
Javaweb之SpringBootWeb案例之AOP核心概念的详细解析
9 0
|
6天前
|
JavaScript 前端开发 测试技术
JavaScript中的函数式编程:纯函数与高阶函数的概念解析
【4月更文挑战第22天】了解JavaScript中的函数式编程,关键在于纯函数和高阶函数。纯函数有确定输出和无副作用,利于预测、测试和维护。例如,`add(a, b)`函数即为纯函数。高阶函数接受或返回函数,用于抽象、复用和组合,如`map`、`filter`。函数式编程能提升代码可读性、可维护性和测试性,帮助构建高效应用。
|
6天前
|
存储 C# 开发者
深入了解 C#编程的核心概念
【4月更文挑战第20天】C#是一种强大的面向对象编程语言,关键概念包括:面向对象(类、对象、继承、多态)、封装、数据类型、变量与常量、控制流、异常处理、委托和事件、泛型及集合。掌握这些概念并通过阅读代码、实践编写和学习新技巧行动,是成为熟练C#开发者的基石。了解并运用这些核心概念能提升代码效率、灵活性和可维护性。

推荐镜像

更多