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


    

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

    对于接口来说,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。你也可以关注我的新浪微博进行交流。

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

  • 相关阅读:
    js获取当前时间,日期格式为年月日
    create-react-app里添加less
    react css 模块化
    react 点击父级元素 不触发子级元素click事件
    react-router-dom 实现路由左右滑动进入页面的效果
    vue路由左右侧滑动 react路由左右侧滑动
    react 父组件不能直接在子组件上加className,也得用props传递过去
    react 父组件调用子组件方法
    css滚动相关问题记录
    javascript异步编程的几种方法
  • 原文地址:https://www.cnblogs.com/yisuowushinian/p/4715612.html
Copyright © 2020-2023  润新知