C#中的接口(interface)

2023-11-15

接口的命名规范

I+名词

接口与抽象类的区别

接口是由抽象类演变而来的。

抽象类是未完全实现逻辑的类,其内部可以有抽象成员,也可以有非抽象成员;且子类在覆写抽象成员时,需要修饰符override。

而接口是完全未实现逻辑的,其内部只允许存在抽象成员而不能包含非抽象成员,即不能包含数据成员和静态成员;且成员是隐式默认public和absolute的,不允许显式地写出来;子类在实现接口的方法时,不需要修饰符override。

接口声明只能包含如下类型的非静态成员函数的声明:

方法、属性、事件、索引器

实现接口

只有类和结构才能实现接口,在实现时应满足:

  • 在基类列表中包括接口名称。
  • 为每一个接口成员提供实现。

一个类可以同时继承一个基类和多个接口,不过在基类列表中基类名称应放置在接口名称之前。

如果在一个类实现的多个接口中,存在两个或多个接口的成员具有相同的签名和返回值类型,那么类可以实现单个成员来满足所有包含重复成员的接口。

接口的作用

让一个方法可以接受各种类型的对象,并且不管该类有什么样的结构。

比如,存在一个方法PrintIHuman(),如果要让其接受Student和Programmer这两个不同的类的对象,就需要让这两个类继承同一个接口:

namespace TestConsole
{
    //声明接口
    interface IHuman
    {
        void Action();
    }
    //声明Student类,其需要实现接口IHuman的方法Action()
    class Student : IHuman
    {
        public void Action()
        {
            Console.WriteLine("The student are studying...");
        }
    }
	//声明Programmer类,其与Student类有着不同的结构,实现Action()方法的方式也不相同
    class Programmer : IHuman
    {
        public String Name { get; set; }
        public void Action()
        {
            Console.WriteLine(Name + " is writing code...");
        }
    }
    //测试:
    class Program
    {
        static void PrintIHuman(IHuman ih)
        {
            ih.Action();
        }
        static void Main(string[] args)
        {
            Student stu = new Student();
            PrintIHuman(stu);

            Programmer prom = new Programmer();
            prom.Name = "Ivan";
            PrintIHuman(prom);
        }
    }
}

运行结果:

The student are studying…
Ivan is writing code…

我们可以这样理解:接口就是一种契约,有些方法只接收遵循某种契约的参数,以上面的代码为例,IHuman接口是一种契约,而PrintIHuman()这个方法只接收遵循IHuman这个契约的参数,当Student和Programmer这两个类都遵循IHuman这个契约时,就可以使用PrintIHuman()方法了。

可以认为,接口是为解耦而生的。例如,当一个方法需要某种契约的参数时,而此时遵守这个契约的类出了bug,在修复期间,方法就可以先去使用其他遵守这个契约的类;如果没有接口的话,这个方法和这个类之间的耦合度就很大了,当类出了问题,方法也会随之瘫痪,加大了维护难度和成本。

使用系统提供的接口

比如Array的sort方法,能对数组进行排序,但如果是我们自己写的类,想让sort方法对我们的类对象根据成员值大小进行排序,那sort是做不到的,除非我们的类实现了 IComparable 接口。

namespace TestConsole
{
    //实现接口:IComparable,要实现其方法:CompareTo
    class MyTest : IComparable
    {
        public int MyVar { get; set; }
        public int CompareTo(object obj)
        {
            MyTest mt = (MyTest)obj;
            if (this.MyVar < mt.MyVar) return -1;
            if (this.MyVar > mt.MyVar) return 1;
            return 0;
        }
    }  
    
    class Program
    {
        //打印数组元素
        static void printArray(MyTest[] mt)
        {
            foreach (var item in mt)
            {
                Console.Write(item.MyVar+" ");
            }
            Console.WriteLine("");
        }
        //测试
        static void Main(string[] args)
        {
            var myInt = new[] { 20, 4, 47, 6, 1 };
            MyTest[] amt = new MyTest[5];
            for (int i = 0; i < 5; i++)
            {
                amt[i] = new MyTest();
                amt[i].MyVar = myInt[i];
            }
            Console.WriteLine("排序前:");
            printArray(amt);
            Array.Sort(amt);
            Console.WriteLine("排序后:");
            printArray(amt);
        }
    } 
}

运行结果:

排序前:
20 4 47 6 1
排序后:
1 4 6 20 47

is和as运算符

接口是引用类型,可以通过强制转换运算符来获取对象接口的引用,但这种操作会抛出异常,我们可以使用as运算符来解决这一问题。

is运算符则用来判断某个对象是否为某个类型。

Student stu = new Student(); //“Student”是实现了接口“IHuman”的类
IHuman ih = stu as IHuman; //类型转换
if (ih is null) //判断对象ih是否为空
{
    Console.WriteLine("ih is null");
}
if (ih is IHuman) //判断对象ih是否为IHuman类型
{
    Console.WriteLine("ih is IHuman");
}
if (ih is Student) //判断对象ih是否为Student类型
{
    Console.WriteLine("ih is Student");
}

运行结果:

ih is IHuman
ih is Student

多个接口的独立引用

如果一个类实现了多个接口,我们可以获取每一个接口的独立引用

namespace TestConsole
{
    interface IStone
    {
        String Color { get; set; }
    }
    interface IMonkey
    {
        void Action();
    }
    
    class StoneMonkey : IStone, IMonkey
    {
        public String Color { get; set; }

        public void Action()
        {
            Console.WriteLine("I can subdue demons.");
        }
    }
    //测试:
    class Program
    {
        static void Main(string[] args)
        {
            StoneMonkey sunWuKong=new StoneMonkey();
            IMonkey falseMonkey = sunWuKong as IMonkey; 
            //falseMonkey独立引用了sunWuKong的IMonkey接口
            //falseMonkey可以使用Action()方法,但无法像sunWuKong一样使用Color属性
            falseMonkey.Action();
            sunWuKong.Color = "golden";
            sunWuKong..Action();
        }
    }
}

显式接口成员实现

前面提到过,单个类的单个方法可以满足多个接口的重复成员,但如果想要为每一个接口分离实现,则需要显式实现接口成员。

显示接口成员实现:使用限定接口名称来声明,由接口名称成员名称以及它们中间的点分隔符号构成。此时public是隐式的,不能显式写出。

namespace TestConsole
{
    interface ITest1
    {
        void Action();
    }
    interface ITest2
    {
        void Action();
    }
    
    class MyClass : ITest1, ITest2
    {
        void ITest1.Action()
        {
            Console.WriteLine("1号接口");
        }

        void ITest2.Action()
        {
            Console.WriteLine("2号接口");
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            MyClass mc = new MyClass(); //MyClass对象mc无法直接调用Action方法
            //可以通过类型转换来调用:
            ((ITest1)mc).Action();
            //或者用接口类型创建的MyClass对象来调用:
            ITest2 it2 = new MyClass();
            it2.Action();
        }
    }
}

运行结果:

1号接口
2号接口

注意,以上的“Action()”不是类级别的方法,所以MyClass类的对象mc无法直接调用它。不过,存在显式接口成员实现时,类级别的实现是允许的,即MyClass类可以同时包含显式接口成员实现和类级别的实现:

class MyClass : ITest1, ITest2
{
    //显式接口成员实现
    void ITest1.Action()
    {
        Console.WriteLine("1号接口");
    }
    void ITest2.Action()
    {
        Console.WriteLine("2号接口");
    }
    //类级别的实现
    public void Action()
    {
        Console.WriteLine("类级别的实现");
    }
}
    
class Program
{
    static void Main(string[] args)
    {
        MyClass mc = new MyClass();
        mc.Action();

        ITest1 it1 = new MyClass();
        it1.Action();

        ITest2 it2 = new MyClass();
        it2.Action();
    }
}

运行结果:

类级别的实现
1号接口
2号接口

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C#中的接口(interface) 的相关文章

随机推荐

  • Spring Boot Maven Plugin -- repackage目标;spring-boot-maven-plugin的executable配置

    Spring Boot Maven Plugin repackage目标 Spring Boot Maven Plugin插件提供spring boot在maven中的支持 允许你打包可运行的jar包或war包 插件提供了几个maven目标
  • nginx server.conf demo

    server access log Users xx software nginx logs access log error log Users xx software nginx logs error log listen 50001
  • 大数据毕设 - 深度学习安全帽佩戴检测系统(python OpenCV yolov5)

    文章目录 0 前言 1 课题背景 2 效果演示 3 Yolov5框架 4 数据处理和训练 4 1 安全帽检测 4 2 检测危险区域内是否有人 5 最后 0 前言 Hi 大家好 这里是丹成学长的毕设系列文章 对毕设有任何疑问都可以问学长哦 这
  • rke2 在线部署 kubernetes

    文章目录 1 还原虚拟机 2 背景 3 介绍 4 预备条件 5 1 配置网卡 5 配置主机名 6 配置互信 7 安装 ansible 8 系统初始化 9 kube master01 部署 9 1 定制配置文件 可选 9 2 部署 9 3 命
  • 微信二维码的生成(java后端)--邀请新人

    目录 写在前言 1 微信官方文档 2 具体分析 写在前言 最近因为在学习微信小程序邀请新用户的功能 所以需要后端生成二维码并且携带本人的用户id或者其他的信息 传给前端 用户通过这个二维码去进行登录或者其他的操作 这时候前端人员记录下来邀请
  • Qt 2D绘图坐标系统

    一 坐标系简介 Qt中每一个窗口都有一个坐标系 默认的 窗口左上角为坐标原点 然后水平向右依次增大 水平向左依次减小 垂直向下依次增大 垂直向上依次减小 原点即为 0 0 点 然后以像素为单位增减 例如 void Dialog paintE
  • Qt之QGraphicsView实战篇

    作者 billy 版权声明 著作权归作者所有 商业转载请联系作者获得授权 非商业转载请注明出处 前言 前面的章节介绍了 Graphics View 绘图架构 终于到实战了 真的是千呼万唤始出来 这一章节就用 Graphics View 绘图
  • 第二章:Matplotlib之艺术画笔见乾坤

    第二章 艺术画笔见乾坤 一 概述 1 matplotlib的三层api matplotlib的原理或者说基础逻辑是 用Artist对象在画布 canvas 上绘制 Render 图形 就和人作画的步骤类似 准备一块画布或画纸 准备好颜料 画
  • Tomcat重启单个服务

    一个tomcat中有多个服务 每次为了重启某个服务 需要重新启动Tomcat 会影响其他服务的正常运行 可通过以下方式进行设置 进入Tomcat服务管理页面 对单个服务进行管理 1 首先我们启动tomcat 访问主页 可以看到右上位置有三个
  • MYSQL--基础--10--慢查询日志

    MYSQL 基础 10 慢查询日志 1 是什么 是MySQL提供的一种日志记录 支持将日志记录写入文件 将SQL查询时间超过 大于 设置阈值的语句 记录到慢查询日志中 2 查看语句 2 1 慢日志是否开启和日志文件位置 show varia
  • 【yolo】yolov5-seg实例分割标签

    文章目录 前言 yolo标签 yolov5 seg标签 转换代码 前言 不同于以往的yolo数据 用的是目标框的label 再加上语义标签 yolov5 seg中用的标签只有一个语义标签 没有目标框的标签 具体对比如下 yolo标签 1 目
  • java 获取月份 几周_java – 我想在特定的月份获得几周的时间

    Calendar类的WEEK OF YEAR属性可能对您有用 参考 Calendar class 创建一个新的日期 将是一个月的第一天 得到这一天的一周的这个星期 让你说起点价值 创建一个新的日期 这将是给定月份的最后一天 获得今年的一周
  • FastCFS核心组件FastStore架构及特点

    FastCFS核心组件FastStore架构及特点 本篇文章转载于 FastCFS 作者 余庆 大佬的 FastDFS分享与交流 公众号 上一篇文章介绍了 FastCFS 服务端两大核心组件 FastDIR 和 FastStore 其中 F
  • python文本数据处理_Python文本数据分析与处理

    Python文本数据分析与处理 新闻摘要 分词 使用jieba分词 注意lcut只接受字符串 过滤停用词 TF IDF得到摘要信息或者使用LDA主题模型 TF IDF有两种 jieba analyse extract tags conten
  • Linux中su、su -和sudo的区别

    su 切换到root用户 但是并没有转到root用户家目录下 即没有改变用户的环境 su 切换到root用户 并转到root用户的家目录下 即改变到了root用户的环境 这个涉及到不同用户下的环境变量的配置 sudo 通过sudo 我们能把
  • 【C++】迭代器

    目录 1 迭代器 1 1 迭代器的操作 1 2 迭代器范围 1 3 使用左闭合范围蕴含的编程假定 2 begin和end成员 3 容器操作可能使迭代器失效 3 1 迭代器失效 3 2 编写改变容器的循环程序 3 3 不要保存end返回的迭代
  • (1)分类算法

    分类算法原理 一 KNN K 近邻 1 定义 如果待推测点 空心点 在中间的某个位置 则计算出与其最邻近的4个样本点 K 4 而此时这4个样本点包含了3个类别 1红 1蓝 2绿 针对这样的情况 knn算法通常采用投票法来进行类别推测 即找出
  • 安装VMware Tools方法(对于 18.04LTS版本)

    大家都知道 每一种虚拟机 如VMware Parallels Desktop等都有一个Tools用来让linux系统和windows系统可以共用剪贴板等工具 使得在windows上复制了的东西 在linux ubuntu 上可以访问 对于V
  • MQ知识梳理

    常见MQ有哪几种 分别适用什么场景 常见的有ActiveMQ RabbitMQ RocketMQ Kafka ActiveMQ比较成熟 功能多 但也比较老 各方面都不突出 目前已很少使用 RabbitMQ性能高 功能多 吞度量万级 有开源活
  • C#中的接口(interface)

    接口的命名规范 I 名词 接口与抽象类的区别 接口是由抽象类演变而来的 抽象类是未完全实现逻辑的类 其内部可以有抽象成员 也可以有非抽象成员 且子类在覆写抽象成员时 需要修饰符override 而接口是完全未实现逻辑的 其内部只允许存在抽象