• C#【高级篇】 C# 接口(Interface)


    C#学习汇总 - 总目录


    前言

    接口定义了所有类继承接口时应遵循的语法合同接口定义了语法合同 “是什么” 部分,派生类定义了语法合同 “怎么做” 部分。

    接口定义了属性、方法和事件,这些都是接口的成员。

    接口只包含了成员的声明
    成员的定义是派生类的责任
    接口提供了派生类应遵循的标准结构

    接口使得实现接口的类或结构体在形式上保持一致

    接口和抽象类的理解:

    抽象类在某种程度上与接口类似,但是,它们大多只是用在当只有少数方法由基类声明由派生类实现时

    接口本身并不实现任何功能,它只是和声明实现该接口的对象订立一个必须实现哪些行为的契约

    抽象类不能直接实例化,但允许派生出具体的,具有实际功能的类。


    一、定义接口

    接口使用 interface 关键字声明,它与类的声明类似。接口声明默认是 public 的。下面是一个接口声明的实例:

    interface IMyInterface
    {
        void MethodToImplement();
    }
    
    • 1
    • 2
    • 3
    • 4

    以上代码定义了接口 IMyInterface。通常接口命令以 I 字母开头,这个接口只有一个方法 MethodToImplement(),没有参数和返回值,当然我们可以按照需求设置参数和返回值。

    值得注意的是,该方法并没有具体的实现

    二、实现接口

    接下来我们来实现以上接口:InterfaceImplementer.cs

    using System;
    
    interface IMyInterface
    {
            // 接口成员
        void MethodToImplement();
    }
    
    class InterfaceImplementer : IMyInterface
    {
        static void Main()
        {
            InterfaceImplementer iImp = new InterfaceImplementer();
            iImp.MethodToImplement();
            Console.ReadLine();
        }
    
        public void MethodToImplement()
        {
            Console.WriteLine("MethodToImplement() called.");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    InterfaceImplementer 类实现了 IMyInterface 接口,接口的实现与类的继承语法格式类似:

    class InterfaceImplementer : IMyInterface
    
    • 1

    继承接口后,我们需要实现接口的方法 MethodToImplement() , 方法名必须与接口定义的方法名一致

    三、接口继承

    以下实例定义了两个接口 IMyInterface 和 IParentInterface。

    如果一个接口继承其他接口,那么实现类或结构就需要实现所有接口的成员。

    以下实例 IMyInterface 继承了 IParentInterface 接口,因此接口实现类必须实现 MethodToImplement() 和ParentInterfaceMethod() 方法:

    using System;
    
    interface IParentInterface
    {
        void ParentInterfaceMethod();
    }
    
    interface IMyInterface : IParentInterface
    {
        void MethodToImplement();
    }
    
    class InterfaceImplementer : IMyInterface
    {
        static void Main()
        {
            InterfaceImplementer iImp = new InterfaceImplementer();
            iImp.MethodToImplement();
            iImp.ParentInterfaceMethod();
            Console.ReadLine();
        }
    
        public void MethodToImplement()
        {
            Console.WriteLine("MethodToImplement() called.");
        }
    
        public void ParentInterfaceMethod()
        {
            Console.WriteLine("ParentInterfaceMethod() called.");
        }
    }
    
    • 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

    实例输出结果为:

    MethodToImplement() called.
    ParentInterfaceMethod() called.
    
    • 1
    • 2

    总结

    • 接口的定义:指定一组函数成员而不实现成员的引用类型,其它类型和接口可以继承接口。
    • 接口是“是什么”的规范,继承类或结构体是“怎么做”的具体实现。
    • 接口的特点:见 补充:一、二
    • 接口的应用场景:见实例1来理解
    • 接口和抽象类的区别

    补充

    一、接口注意的几点:

    • 接口方法不能用public abstract等修饰。接口内不能有字段变量,构造函数。
    • 接口内可以定义属性(有get和set的方法)。如string color { get ; set ; }这种。
    • 实现接口时,必须和接口的格式一致。
    • 必须实现接口的所有方法。

    二、接口的定义是指定一组函数成员而不实现成员的引用类型,其它类型和接口可以继承接口。接口主要有以下特点:

    1. 通过接口可以实现多重继承,C# 接口的成员不能有 public、protected、internal、private 等修饰符。原因很简单,接口里面的方法都需要由外面接口实现去实现方法体,那么其修饰符必然是 public。C# 接口中的成员默认是 public 的。
    2. 接口成员不能有 new、static、abstract、override、virtual 修饰符。有一点要注意,当一个接口实现一个接口,这2个接口中有相同的方法时,可用 new 关键字隐藏父接口中的方法
    3. 接口中只包含成员的签名,接口没有构造函数,所以不能直接使用 new 对接口进行实例化。接口中只能包含方法、属性、事件和索引的组合。接口一旦被实现,实现类必须实现接口中的所有成员,除非实现类本身是抽象类。
    4. C# 的类是单继承,接口是解决 C# 里面类可以同时继承多个基类的问题。

    实例1:【加深接口的理解】

    每个员工的属性、方法等都是相似的,这时就可以规范一个接口,而不是每个员工都搞个类。

    class Program
    {
        static void Main(string[] args)
        {
            IWorker james1 = new James1();
            IWorker james2 = new James2();
            james1.work("设计");
            james2.work("编程");
            //从这个例子我体会到了有接口的好处,可以想象如果又来了新的员工。
            //如果不采用接口,而是每个员工都有一个单独的类,这样就会容易出错。
            //如果有接口这种协议约束的话,那么只要实现了接口就肯定有接口里声明的方法,我们只需拿来调用。
            Console.ReadLine();
        }
    }
    public interface IWorker{ void work(string s); }
    class James1 : IWorker
    {
        public void work(string s)
        {
            Console.WriteLine("我的名字是James1,我的工作是" +s);
        }
    }
    class James2 : IWorker
    {
        public void work(string s)
        {
            Console.WriteLine("我的名字是James2,我的工作是"+s);
        }
    }
    
    • 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

    实例2【重要:结构体实现接口后是值类型还是引用类型?】

    在C#中数据类型分为值类型和引用类型
    值类型:结构体、枚举
    引用类型:类、接口、数组、委托

    在结构使用时,结构可以实现接口。这时就有一个问题“结构体实现接口后是值类型还是引用类型?”。带着这个问题,我们看下面这段代码。

    using System;
    
    namespace ConsoleApp1
    {
        //结构体班级   
        struct StructClass : IClass
        {
            public int Count;//人数   
            public void AddStudent()
            {
                Count++;
            }
    
            public void ShowCount()
            {
                Console.WriteLine(Count);
            }
    
        }
        //接口   
        interface IClass
        {
            void AddStudent();//添加学生   
            void ShowCount();//显示学生人数   
        }
        class Program
        {
            static void Main(string[] args)
            {
                StructClass s1 = new StructClass();
                StructClass s2 = s1;
                s1.AddStudent();
                s1.ShowCount(); //输出1   
                s2.ShowCount(); //输出0   
                //说明s2和s1不指向同一个对象,s2=s1是创建了一个s1的副本 
                //这是值类型很显著的标志   
    
                IClass ic1 = new StructClass();
                IClass ic2 = ic1;
                ic1.AddStudent();
                ic1.ShowCount();//输出1   
                ic2.ShowCount();//输出1   
                //说明s2和s1指向同一个对象,s2=s1是将s1的引用赋给s2   
                //这是引用类型很显著的标志   
                Console.ReadLine();
            }
        }
    }
    
    
    • 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

    运行结果:

    1
    0
    1
    1
    
    • 1
    • 2
    • 3
    • 4

    最后得出结论:
    当我们声明对象(s1、s2)是结构体类型时,对象是值类型,对象在栈中创建
    当我们声明对象(ic1、ic2)是接口类型时,对象是引用类型,对象在堆中创建

    C#学习汇总 - 总目录

  • 相关阅读:
    前端工程师应该如何去创业?
    Vue2 Element description组件 列合并
    每日OJ题_算法_双指针_力扣283. 移动零+力扣1089. 复写零
    Mac版AndroidStudio常用快捷键(汇总)
    使用pro-components遇到的问题
    Logrus 集成 color 库实现自定义日志颜色输出字符原理
    面试官:Redis 过期删除策略和内存淘汰策略有什么区别?
    JD(按关键字搜索商品)API接口
    pixel2的root过程
    墨迹天气商业版UTF-8模板,Discuz3.4灰白色风格(带教程)
  • 原文地址:https://blog.csdn.net/sinat_40003796/article/details/125520392