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   老菜农  阅读(26)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了

导航

统计信息

点击右上角即可分享
微信分享提示