• C#:接口


    接口:接口是指定了一组函数成员,但却没有实现它们的一种引用类型。

    如何定义和使用接口:

    • interface关键字修饰 接口类型名以大写I开头(如:IList)
    • 接口侧重"我能干什么...",微软给我们预定义的接口就多以xxxable结尾。
    namespace InterfaceDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                IPrintable blackPrinter = new BlackPrinter();//接口和抽象类一样也不能new哦
                blackPrinter.Print();
    
                IPrintable multiColorPrinter = new MultiColourPrinter();
                multiColorPrinter.Print();
    
                Console.ReadLine();
            }
        }
    
        interface IPrintable
        {
            void Print();//不能用public修饰、也不能用abstract修饰
        }
    
        class BlackPrinter : IPrintable
        {
            //不要用override修饰
            public void Print()
            {
                Console.WriteLine("打印出黑白的文档");
            }
        }
    
        class MultiColourPrinter : IPrintable
        {
            public void Print()
            {
                Console.WriteLine("打印出彩色的文档");
            }
        }
    }
    /**输出:
    打印出黑白的文档
    打印出彩色的文档
    **/
    

    以上我们演示了如何定义接口;如何定义类类型来实现接口;并且演示了如何使用接口。

    使用接口的时候需要注意:

    • 接口中的函数成员默认是公开的;且不能被public修饰
    • 接口中的函数成员也不可以被abstract修饰;因为接口中成员是"完全抽象的",它只是定义了某种能力或行为;而具体的能力或行为就交给子类去实现;
    • 若普通类型继承了接口,那么接口中的所有成员,都要被实现;但是在子类中这些被实现的函数成员不需要加override关键修饰;
    • 若抽象类继承了接口,则函数成员可以不被实现;如果在抽象类类中不实现该函数成员,则函数成员必须被标记为 public abstract类型--public是因为该函数成员从接口中继承过来,在子类中无法修改访问修饰符;abstract是因为,只有abstract才能使函数成员有"延迟到子类实现"的功能。

    根据接口的以上特点,我们演示一下以抽象类继承接口:

    interface IPrintable
    {
        void Print();//不能用public修饰、也不能用abstract修饰
    }
    
    abstract class AbstractPrinter : IPrintable
    {
        public abstract void Print();
    }
    
    class MordenPrinter : AbstractPrinter
    {
        public override void Print()
        {
            Console.WriteLine("激光打印,全业务支持");
        }
    }
    

    下面将展示如何用接口解决实际问题:

    问题:借助Array类型的Sort()方法,我们可以对数组中方便地进行排序(如下代码所示:)

    class Program
    {
        static void Main(string[] args)
        {
            int[] intArr = new int[] { 2, 5, 1, 3, 4, 9 };
            Array.Sort(intArr);
            foreach (var item in intArr)
            {
                Console.WriteLine(item);
            }
            Console.ReadLine();
        }
    }
    /*输出:
    1
    2
    3
    4
    5
    9*/
    

    但是,如果数组中的元素是我们自定义的类型时,Sort()方法就不那么好使了:

    class Program
    {
        static void Main(string[] args)
        {
            Student tangsan = new Student() { StuName = "唐三", StuNumber = 003 };
            Student xiaowu = new Student() { StuName = "小舞", StuNumber = 004 };
            Student pangzi = new Student() { StuName = "胖子", StuNumber = 002 };
            Student daimubai = new Student() { StuName = "戴沐白", StuNumber = 001 };
            Student[] students = new Student[] { tangsan, xiaowu, pangzi, daimubai };
            Array.Sort(students);
            foreach (var item in students)
            {
                Console.WriteLine(item.StuNumber+"--"+item.StuName);
            }
            Console.ReadLine();
        }
    }
    
    class Student
    {
        public int StuNumber { get; set; }
        public string StuName { get; set; }
    }
    /*运行报错--未经处理的异常:System.InvalidOperationException:“未能比较数组中的两个元素。”*/
    

    打开错误详细信息得知,原来是因为:我们自定义的类型没有实现ICompare接口,所以数组中元素之间不具有可比性。

    我们要实现这个接口,首先得了解这个接口吧?

    查找Icompare接口的定义可知:

    Icopmare接口中只定义了一个函数成员;该函数接受一个object类型的引用;若当前实例和obj比较结果小于零 则当前实例排在obj之前;大于零 则当前实例排在obj之后...

    public interface IComparable
    {
        int CompareTo(object obj);
    }
    

    知道了接口的定义,我们就可以动手了:继承ICompare接口,实现CompareTo方法:

    class Student:IComparable
    {
        public int StuNumber { get; set; }
        public string StuName { get; set; }
    
        public int CompareTo(object obj)
        {
            Student student = obj as Student;
            if (this.StuNumber>student.StuNumber)
            {
                return 1;
            }
            if (this.StuNumber<student.StuNumber)
            {
                return -1;
            }
            return 0;
        }
    }
    /*运行结果:
    1--戴沐白
    2--胖子
    3--唐三
    4--小舞
    */
    

    通过将我们自定义的类型实现ICompare接口,并在CompareTo方法中自定义了比较规则,便可以是我们自定义的类型可以排序。

    //当然你也可以通过稍微改变规则,得到降序结果
    class Student : IComparable
    {
        public int StuNumber { get; set; }
        public string StuName { get; set; }
    
        public int CompareTo(object obj)
        {
            Student student = obj as Student;
            if (this.StuNumber > student.StuNumber)
            {
                //一改常态,当前实例大于obj时,返回一个大于0的值,这样可以让当前实例排在前面~~~
                return -1;
            }
            if (this.StuNumber < student.StuNumber)
            {
                return 1;
            }
            return 0;
        }
    }
    /*输出结果:
    4--小舞
    3--唐三
    2--胖子
    1--戴沐白
    */
    

    倒过来想一下,为什么一开始的时候int[] 数组就可以支持Sort()方法?通过查看int类型的定义,就明白了:


    原来C#给我们提供的Int32结构体类型,已然实现了ICompare接口,并且重写了CompareTo方法。

    在此之前我们有一个默认共识:每种类型(除object外)都只有一个基类--即类的继承具有单根性;但是对于接口而言,一个类却可以实现多个接口。

    比如:有一天你在做一道计算题,这道计算题需要计算器;但是你手边没有计算器,同桌莉哥借给你一个MAC笔记本电脑,并告诉你:用这个,它也可以当计算器还可以上网看我直播呢!!!

    我们将上面的场景 转化成下面的代码:
    namespace InterfaceDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                //当你有计算器时
                ICaculatable caculator = new CaculatorMachine();
                caculator.Caculator();
    
                //当你没有计算器,同桌借给你一个苹果笔记本
                IComputer macBook = new MacBook();
                macBook.Caculator();
    
                Console.ReadLine();
            }
        }
    
        interface ICaculatable
        {
            void Caculator();//计算题
        }
    
        class CaculatorMachine : ICaculatable
        {
            public void Caculator()
            {
                Console.WriteLine("做计算题...");
            }
        }
    
        interface IComputer
        {
            void Caculator();//计算题
            void SeeLive();//看直播
            void PalyGame();//玩游戏
        }
    
        class MacBook : IComputer
        {
            public void Caculator()
            {
                Console.WriteLine("用苹果笔记本,做计算题...");
            }
    
            public void PalyGame()
            {
                Console.WriteLine("玩CSGO.....");
            }
    
            public void SeeLive()
            {
                Console.WriteLine("看莉哥直播.....");
            }
        }
    }
    /*输出结果:
    做计算题...
    用苹果笔记本,做计算题...
    */
    

    看到用上了苹果笔记本,你会不会大呼奢侈~~~;回到正题,我们不是要讲一个类继承多个接口嘛?

    通过观察上面的代码,我们发现;在IComputer、ICaculator中都声明了Caculator();假设我们现在买电脑都是为了追剧、看直播、玩吃鸡。。。那么对于IComputer接口来说,Caculator()就显得多余;我们说这样的现象:其实是接口定义的过"胖"了;
    • 将接口IComputer中的Caculator()成员移除、留下"更有意义"的成员(如:玩游戏、看直播等等...)
    • 让我们MacBook类既实现ICaculator接口、也实现IComputer 接口
    • 这样 MacBook 既可以计算又不耽误玩游戏、看直播
    namespace InterfaceDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                //用同桌借给你的苹果笔记本 做计算题
                ICaculatable macCaculator = new MacBook();
                macCaculator.Caculator();
    
                //用同桌借给你的苹果笔记本 玩游戏 看直播
                IComputer macBook = new MacBook();
                macBook.PalyGame();
                macBook.SeeLive();
    
                Console.ReadLine();
            }
        }
    
        interface ICaculatable
        {
            void Caculator();//计算题
        }
    
        class CaculatorMachine : ICaculatable
        {
            public void Caculator()
            {
                Console.WriteLine("做计算题...");
            }
        }
    
        interface IComputer
        {
            void SeeLive();//看直播
            void PalyGame();//玩游戏
        }
    
        class MacBook : IComputer, ICaculatable
        {
            public void Caculator()
            {
                Console.WriteLine("用苹果笔记本,做计算题...");
            }
    
            public void PalyGame()
            {
                Console.WriteLine("玩CSGO.....");
            }
    
            public void SeeLive()
            {
                Console.WriteLine("看莉哥直播.....");
            }
        }
    }
    /*输出结果:
    用苹果笔记本,做计算题...
    玩CSGO.....
    看莉哥直播.....
    */
    

    通过以上例子,我们得知:

    • 类可以实现多个接口;
    • 通过将接口的功能细化,避免设计出胖接口,可以使我们功能设计更合理--这其实就是设计模式六大原则(SOLID)中的接口隔离原则(ISP)

    以上便是对接口类型的总结,记录下来以便以后查阅。

  • 相关阅读:
    java-初始化和清理
    java-字符串
    java-I/O流
    java-反射和代理
    java-执行流程控制语句
    java-访问控制修饰符
    java-异常
    java-注解
    [ Java学习 ] 一道Java好题的详细题解 001
    [ Java学习 ] 查阅资料整理 002
  • 原文地址:https://www.cnblogs.com/bigbosscyb/p/13922903.html
Copyright © 2020-2023  润新知