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)

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

原文地址:https://www.cnblogs.com/bigbosscyb/p/13922903.html