C#学习笔记 -- 接口

接口

1、什么是接口

  • 接口指定一组函数成员, 而不实现他们的引用类型

  • 只能类和结构来实现接口

例子
//声明实现接口的CA类
class CA : IInfo
{
    public string Name;
    public int Age;
​
    //在类中实现接口的方法
    public string GetAge()
    {
        return Age.ToString();
    }
​
    public string GetName()
    {
        return Name;
    }
}
class CB : IInfo
{
    public string First;
    public string Last;
    public double PersonsAge;
​
    public string GetAge()
    {
        return PersonsAge.ToString();
    }
​
    public string GetName()
    {
        return First + "·" + Last;
    }
}
public interface IInfo
{
    string GetName();
    string GetAge();
}
public static class Printer
{
    public static void PrintInfo(IInfo item)
    {
        Console.WriteLine($"名字: {item.GetName()}, 年龄: {item.GetAge()}");
    }
}
static void Main(string[] args)
{
    CA a = new CA() { Name = "John Doe", Age = 35 };
    CB b = new CB() { First = "James", Last = " Doe", PersonsAge = 33 };
    PrintInfo(a);
    PrintInfo(b);
}

(1)使用IComparable接口

  • Array.Sort()依赖于IComparable的接口

    public interface IComparable
    {
        int CompareTo(Object obj);
    }
  • CompareTo()方法返回值如下

    • 负数: 如果当前对象小于参数对象

    • 正数: 如果当前对象大于参数对象

    • 0: 两个对象相对

  • Sort()使用的算法是依赖与使用对象的CompareTo()来决定两个元素的次序, int类型实现了IComparable, 但是自定义类没有, 因此自定义类的数组无法使用Sort()

  • 让类实现IComparable接口, 让Sort方法可以用于自定义类型的对象

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

(2)实现一个接口, 类和接口必须要做的事情

  • 在基类列表中列出接口名称

  • 为接口的每个成员提供实现

2、声明接口

声明接口重要事项

  • 接口可以使用任何访问修饰 public、protected、internal、private

  • 接口成员是隐式public的, 不允许用任何访问修饰符, 包括public

  • 接口声明不能包含以下成员

    • 数据成员 (字段)

    • 静态成员

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

    • 方法

    • 属性

    • 事件

    • 索引器

  • 这些函数成员的声明不能包含任何实现代码, 必须使用分号代替每一个成员声明的主体

  • 接口名称必须以大写的I开头

  • 与类和结构一样, 接口声明也可以分割成分部接口声明

访问修饰符 interface IMyInterface1
{
    //不能有字段和静态成员
    //成员不能用任何访问修饰符
    Type DoStuff(Type nVar1, Type lVar2);
}

3、实现接口

只有类和结构才能实现接口, 要实现接口, 必须有如下

  • 在基类列表中包括接口名称

  • 为每一个接口成员提供实现

注意
  1. 如果类实现了接口, 他必须实现接口所有成员

  2. 如果类派生自基类并实现了接口, 基类列表中的基类名称必须放在接口之前

class Derived : MyBaseClass, IIfc1, IIfc2
{
    //...
}

4、接口是引用类型

  • 不能直接通过类对象的成员访问接口

  • 可以通过把类对象引用强制转换为接口类型来获取指向接口的引用

  • 一旦有了接口的引用, 就可以使用.来调用接口的成员, 使用这个接口, 你不能调用不属于这个接口成员的类成员

class MyClass : IIfc1
{
    public void PrintOut(string s)
    {
        //...
    }
}
interface IIfc1
{
    void PrintOut(string s);
}
static void Main(string[] args)
{
    MyClass mc = new MyClass(); //创建类对象
    mc.PrintOut("object");//调用对象的实现方法
    
    IIfc1 ifc = (IIfc1)mc;//转换为接口类型的引用
    ifc.PrintOut("iterface");//调用接口方法
}

5、接口和as运算符

可以使用强制转换运算符来获取对象接口的引用, 另一个更好的方式是as运算符

如果将类对象引用强制转换为类未实现的接口的引用, 强制转换操作为抛出异常, 可以使用as运算符来避免这个问题

接口名称 接口引用 = 类对象引用 as 接口名称
  • 如果类实现了接口, 表达式返回指向接口的引用

  • 如果类没有实现接口, 表达式返回null而不是抛出异常

ILiveBirth b = a as ILiveBirth;
if(b != null)
{
    Console.WriteLine($"{b.BabyCalled()}");
}

6、实现多个接口

  • 类或结构可以实现任意数量的接口

  • 所有实现的接口必须列在基类列表中并以逗号分割(如果有基类名称, 则在其之后)

class MyData : IDataRetrieve, IDataStore
{
    ...
}

7、实现有重复成员的接口

由于类可以实现任意数量的接口, 有可能有两个或多个接口成员具有相同的签名和返回类型

interface IIfc1
{
    void PrintOut(string s);
}
interface IIfc2
{
    void PrintOut(string s);
}
  • 如果一个类实现了多个接口, 并且其中一些接口成员具有相同的签名和返回类型

  • 那么类可以实现单个成员来满足所有包含重复成员的接口

class MyClass : IIfc1, IIfc2
{
    public void PrintOut(string s)
    {
        Console.WriteLine($"PrintOut:{s}");
    }
}
static void Main(string[] args)
{
    MyClass mc = new MyClass();
    mc.Print("object");
}

8、多个接口的引用

  • 可以通过对象强制转换为接口类型的引用, 来获取一个指向接口的引用

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

interface IIfc1
{
    void PrintOut(string s);
}
interface IIfc2
{
    void PrintOut(string s);
}
class MyClass : IIfc1, IIfc2
{
    public void PrintOut(string s)
    {
        Console.WriteLine($"s = {s}");
    }
}
static void Main(string[] args)
{
    MyClass mc = new MyClass();
    IIfc1 ifc1 = (IIfc1) mc;
    IIfc2 ifc2 = (IIfc2) mc;

    mc.PrintOut("object");
    ifc1.PrintOut("Interface1");
    ifc2.PrintOut("Interface2");
}

9、派生成员作为实现

实现接口的类可以从它的基类继承实现的代码

interface IIfc1
{
    void PrintOut(string s);
}
class MyBaseClass
{
    public void PrintOut(string s)
    {
        Console.WriteLine($"{s}");
    }
}
class Derived : MyBaseClass, IIfc1
{

}
static void Main(string[] args)
{
    Derived derived = new Derived();
    derived.PrintOut("derived");//dervied
}
  • 父类实现了某个接口的方法

  • 子类继承父类, 并且实现接口

  • 子类中不实现接口方法, 就可以调用

10、显示接口成员实现

  • 为每一个接口分离实现, 可以创建显示接口成员

  • 与所有接口实现想死, 位于实现了接口的类或结构中

  • 使用限定接口名称来声明, 由接口名称和成员名称以及他们中间的点来分隔符号构成

访问修饰符 返回类型 接口.方法(参数列表)
{
    实现
}
interface IIfc1
{
    void PrintOut(string t);
}
interface IIfc2
{
    void PrintOut(string s);
}
class MyClass1610 : IIfc1, IIfc2
{
    void IIfc1.PrintOut(string t)
    {
        Console.WriteLine($"IIfc1: {t}");
    }
    void IIfc2.PrintOut(string s)
    {
        Console.WriteLine($"IIfc2: {s}");
    }
}
static void Main(string[] args)
{
    MyClass1610 mc = new MyClass1610();
    Interface1610.IIfc1 iifc1 = (Interface1610.IIfc1) mc;
    iifc1.PrintOut("iifc1");

    Interface1610.IIfc2 iifc2 = (Interface1610.IIfc2) mc;
    iifc1.PrintOut("iifc2");

}
注意
  1. 在接口方法没有指向类级别实现, 而是包含了自己的代码

  2. 不能使用MyClass1610的对象来调用方法, 不存在类级别的Printout方法

如果有显示接口成员实现, 类级别的实现是允许的, 担不是必须的, 显示实现满足了类火结构必须实现方法的需求, 因此, 可以有如下三种实现场景

  • 类级别实现

  • 显示接口成员实现

  • 类级别和显示接口成员实现

访问接口成员实现

显示接口成员只能通过接口指向的引用来访问, 其他的类成员都不可用直接访问他们, 必须转换为接口引用

class MyClass1610 : IIfc1
{
    void IIfc1.PrintOut(string t)
    {
        Console.WriteLine($"IIfc1: {t}");
    }
    
    public void Method1()
    {
        //编译错误
        //PrintOut("x");
        //this.PrintOut("x");
        //转换为接口引用
        ((IIfc1)this).PrintOut("xxx");

    }
}

这个限制对继承产生类重要的影响, 由于其他类成员不能直接访问显示接口成员实现, 派生类的成员也不能直接访问他们

11、接口可以继承接口

  • 要指定某个接口继承其他接口, 形式如下

    interface 派生接口 : 基接口1, 基接口2
    {
        ...
    }
  • 类的基类只能有一个, 而接口可以有多个任意基接口

    • 列表中的接口本身可以继承其他接口

    • 结果接口包含它声明的所有成员和基接口的所有成员

interface IDataRetrieve
{
    int GetData();
}
interface IDataStore
{
    void SetData(int x);
}
interface IData : IDataRetrieve, IDataStore
{

}
class MyData : IData
{
    int nPrivateData;
    public int GetData()
    {
        return nPrivateData;
    }

    public void SetData(int x)
    {
        nPrivateData = x;
    }
}
static void Main(string[] args)
{
    MyData myData = new MyData();
    myData.SetData(5);
    Console.WriteLine($"{myData.GetData()}");
}

12、不同类实现一个接口

interface ILiveBirth
{
    string BabyCalled();
}
class Animal
{

}
class Dog : Animal, ILiveBirth
{
    public string BabyCalled()
    {
        return "puppy";
    }
}
class Cat : Animal, ILiveBirth
{
    public string BabyCalled()
    {
        return "pussy";
    }
}

posted on 2023-06-27 10:48  老菜农  阅读(20)  评论(0编辑  收藏  举报

导航