C# 接口(什么是接口)

发布于:2025-07-08 ⋅ 阅读:(17) ⋅ 点赞:(0)

什么是接口

接口是指定一组函数成员而不实现它们的引用类型。所以只能类和结构来实现接囗。这种描
述听起来有点抽象,因此先来看看接口能够帮助我们解决的问题,以及是如何解决的。

以下面的代码为例。观察program类中的Main方法,它创建并初始化了一个CA类的对象,
并将该对象传递给PrintInfo方法。PrintInfo需要一个CA类型的对象,并打印包含在该对象
内的信息。

class CA
{
    public string Name;
    public int Age;
}

class CB
{
    public string First;
    public string Last;
    public double PersonsAge;
}

class Program
{
    static void PrintInfo(CA item)
    {
        Console.WriteLine($"Name:{item.Name},Age:{item.Age}");
    }

    static void Main()
    {
        CA a=new CA() {Name="John Doe",Age=35};
        PrintInfo(a);
    }
}

只要传入的是CA类型的对象,PrintInfo方法就能工作正常。但如果传入的是CB类型的对
象(同样见上面的代码),就不行了。假设PrintInfo方法中的算法非常有用,我们想用它操
作不同类的对象。

现在的代码不能满足上面的需求,原因有很多。首先,PrintInfo的形参指明了实参必须为
CA类型的对象,因此传人CB或其他类型的对象将导致编译错误。但即使能克服这个障碍,使其
接受CB类型的对象还是会有问题,因为CB的结构与CA的不同。其字段的名称和类型与不一
样,PrintInfo对这些字段一无所知。

能不能创建一个能够成功传人PrintInfo的类,并且不管该类是什么样的结构,PrintInfo
都能正常处理呢?接口使这种设想变为可能。
图16-1中的代码使用接口解决了这一问题。你现在不需要理解细节,但一般来说,注意以下几点。

  • 首先,它声明了一个llnfo接口,其中包含两个方法一GetName和GetAge,每个方法
    都返回string。
  • 类CA和CB各自实现了llnfo接口(将其放到基类列表中),并实现了该接口所需的两个
  • Main创建了CA和的实例,并传人PrintInfo。
  • 由于类实例实现了接口,PrintInfo可以调用那两个方法,每个类实例执行各自的方法,
    就好像是执行自己类声明中的方法。
interface IInfo       //声明接口
{
    string GetName();
    string GetAge();
}

class CA:IInfo  //声明实现接口的CA类
{
    public string Name;
    public int Age;
    public string GetName(){return Name;}  //在CA类中实现两个接口方法
    public string GetAge(){return Age.ToString();}
}

class CB:IInfo  //声明实现接口的CB类
{
    public string First;
    public string Last;
    public double PersonsAge;
    public string GetName(){return First+","+Last;}  //在CB类中实现两个接口方法
    public string GetAge(){return PersonsAge.ToString();}
}

class Program
{
    static void PrintInfo(IInfo item)  //传入接口的引用
    {
        Console.WriteLine("Name:{0},Age {1}",item.GetName(),item.GetAge());
    }

    static void Main()
    {
        CA a=new CA(){Name="John Doe",Age=35};
        CB b=new CB(){First="Jane",Last="Doe",PersonsAge=33};

        PrintInfo(a);    
        PrintInfo(b);         //对象的引用能自动转换为他们的接口的引用
    }
}

使用IComparable接口的示例
我们已经了解了接口能够解决的问题,接下来看第二个示例并深入一些细节。先来看看如下
代码,它接受了一个没有排序的整数数组并且按升序进行排序。这段代码的功能如下:

  • 第一行代码创建了包含5个无序整数的数组;
  • 第二行代码使用了Array类的静态sort方法来对元素排序;
  • 用foreach循环输出它们,显示以升序排序的数字。

Array类的sort方法在int数组上工作得很好,但是如果我们尝试在自己的类数组上使用会
发生什么呢?如下所示:

class MyClass  //声明一个简单类
{
    public int TheValue;
}

...
MyClass[] mc=new MyClass[5]; //创建一个有5个元素的数组
                             //创建并初始化元素

Array.Sort(mc);              //尝试使用Sort时抛出异常

如果你尝试运行这段代码的话,不会进行排序而是会得到一个异常。sort不能针对Myclass
对象数组进行排序的原因是,它不知道如何比较用户定义的对象以及如何进行排序。Array类的
sort方法其实依赖于一个叫作1Comparable的接口,它声明在BCL中,包含唯一的方法CompareTo。

下面的代码展示了IComparable接口的声明。注意,接口主体内包含CompareTo方法的声明,
指定了它接受一个object类型的参数。尽管该方法具有名称、参数和返回类型,却没有实现。
它的实现用一个分号表示。

image
图16-2演示了IComparable接口。CompareTo方法用灰色框显示,以表明它不包含实现。

展示IComparable接口

尽管在接口声明中没有为CompareTo方法提供实现,但IComparable接口的.NET文档中描述
了该方法应该做的事情,你可以在创建实现该接口的类或结构时参考。文档中写道,在调用
方法时,它应该返回以下几个值之一

  • 负数值如果当前对象小于参数对象;
  • 正数值如果当前对象大于参数对象;、
  • 零如果两个对象在比较时相等。

Sort使用的算法依赖于使用元素的CompareTo方法来决定两个元素的次序。int类型实现了
lcomparable,但是myClass没有,因此当Sort尝试调用MyClass不存在的CompareTo方法时会
抛出异常。

我们可以通过让类实现IComparable,让Sort方法可以用于MyClass类型的对象。要实现一
个接口,类或结构必须做两件事情:

  • 在基类列表中列出接口名称;
  • 为接口的每一个成员提供实现。
    例如,下面的代码更新了MyClass来实现IComparab1e接口。注意下面关于代码的内容。
  • 接口名称列在类声明的基类列表中。
  • 类实现了一个名为compareTo的方法,它的参数类型和返回类型与接口成员一致。
  • 实现CompareTo方法以遵循接口文档的定义。也就是说,它将它的值与传人方法的对象值
    进行比较,并据此返回一1、1或0。
class MyClass:IComparable  //基类列表中的接口名称
{
    public int ThValue;

    public int CompareTo(Object obj)  //接口方法的实现
    {
        MyClass mc=(MyClass)obj;
        if(this.ThValue<mc.ThValue) return -1;
        if(this.ThValue>mc.TheValue) return -1;
        return 0;
    }
}

图16-3演示了更新后的类。从有阴影的接口方法指向类方法的箭头表示接口方法不包含代
码,而是在类级别的方法中实现。

在MyClass中实现IComparal

下面显示了更新后的完整代码,现在可以使用sort方法来排序MyClass对象数组了。Main
创建并初始化了MyClass对象的数组并且输出它们,然后调用了sort并重新输出,以显示它们
已经排好序了。

class MyClass:IComparable   //类实现接口
{
    public int TheValue; 
    public int CompareTo(object obj)  //实现方法
    {
        MyClass mc=(MyClass)obj;
        if(this.TheValue<mc.TheValue)return -1;
        if(this.TheValue>mc.TheValue) return -1;
        return 0;
    }
}

class Program{
    static void PrintOut(string s,MyClass[] mc)
    {
        Console.Write(s);
        foreach(var m in mc)
            console.Write($"{m.TheValue}");
        Console.WriteLine("");
    }
}

static void Main()
{
    var myInt=new []{20,4,16,9,2};

    MyClass[] mcArr=new MyClass[5];    //创建Myclass对象
    for(int i=0;i<5;i++)
    {
        mcArr[i]=new MyClass();
        mcArr[i].TheValue=myInt[i];
    }

    PrintOut("Initial Order:",mcArr);//输出初始数组
    Array.Sort(mcArr);               //数组排序
    PrintOut("Sorted Order:   ",mcArr);//输出排序后的数组
}