第16章 接口 笔记
第16章 接口 笔记 25.06.22
16.1 什么是接口
接口是声明一组函数成员,而不进行实现的引用类型,只能用类和结构来实现接口。
一个函数内不能传入多个类的实例做为参数,可以通过接口来实现。
// 声明接口
interface IInfo
{string GetName();string GetAge();
}// 声明实现接口的CA类
class CA:IInfo
{public string Name;public int Age;public string GetName() {return Name;}public string GetAge() {return Age.ToString();}
}class CB:IInfo
{public string First;public string Last;public double PersonsAge;public string GetName() {return First + " " + Last;}public string GetAge() {return PersonAge.ToString();}
}class Programe
{// 传入接口的引用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", PersonAge = 33};// 对象的引用自动转化为 它们实现的接口的引用PrintInfo(a);printInfo(b);}
}// outputName: John Doe, Age 35Name: Jane Doe, Age 33
使用 IComparable 接口的示例
整数数组排序
var myInt = new [] { 20, 4, 16, 9, 2 };
Array.Sort(myInt);
foreach (var i in myInt) Console.Write($"{ i } ");// This code produces the following output
2 4 9 16 20
Array类的Sort方法无法对对象数组进行排序,它不知道如何比较对象以及如何进行排序。
Array 类的 Sort 方法依赖于 IComparable 接口,其声明在 BCL 中,只包含唯一的 CompareTo 方法,该方法目前未实现。
IComparable接口 示例
public interface IComparable
{int CompareTo( object obj );
}
调用 CompareTo 方法时,应该返回如下值:
-
负数:当前对象 < 参数对象。
-
正数:当前对象 > 参数对象。
-
0:两个对象相等。
为自定义类实现 IComparable 接口后,就可以使用 Array.Sort 方法进行排序
要实现一个接口,类或结构必须做两件事:
在基类类别中列出接口的名称
为接口的每一个成员提供实现
// 类实现接口
class MyClass : IComparable
{public int TheValue;public int CompareTo(object obj) // Implementation of interface method{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 };// Create array of MyClass objs.MyClass[] mcArr = new MyClass[5];// Initialize the array.for (int i = 0; i < 5; i++) {mcArr[i] = new MyClass();mcArr[i].TheValue = myInt[i];}PrintOut("Initial Order: ", mcArr); // Print the initial array.// Sort the array.Array.Sort(mcArr); PrintOut("Sorted Order: ", mcArr); // Print the sorted array.}
}// output
Initial Order: 20 4 16 9 2
Sorted Order: 2 4 9 16 20
16.2 声明接口
接口声明不能包含以下成员:
-
数据成员。
-
静态成员。
接口声明只能包含如下类型的非静态成员:
-
方法。
-
属性。
-
事件。
-
索引器。
函数声明不能包含任何实现代码,使用分号代替函数主体。
接口名称必须从大写的 I 开始。
可以声明分部接口。
interface IMyInterface1
{ //Semicolon in place of bodyint DoStuff ( int nVar1, long lVar2 );double DoOtherStuff( string s, long x );
}
接口和接口成员的访问性有一些不同:
接口声明可以有任何的访问修饰符。
接口成员是隐式 public,不能有任何访问修饰符。
// 访问修饰符 可以用:public、protected、private、internal
public interface IMyInterface2
{// 错误private int Method1( int nVar1, long lVar2 );
}
16.3 实现接口
要实现接口,类和结构必须:
- 在基类列表中包含接口名称。
- 为每一个接口成员提供实现。
class MyClass: IMyInterface1
{int DoStuff ( int nVar1, long lVar2 ){ // ... } double DoOtherStuff( string s, long x ){ // ... }
}
重要事项如下:
- 必须实现接口的所有成员。
- 基类名称必须放在所有接口之前。
// 基类名、接口名
class Derived : MyBaseClass, IIfc1, IEnumerable, IComparable
{...
}
简单接口的示例
// Declare interface.
interface IIfc1
{ // Semicolon in place of bodyvoid PrintOut(string s);
}//Implement interface
class MyClass : IIfc1 // Declare class.
{public void PrintOut(string s) // Implementation{Console.WriteLine($"Calling through: {s }");}
}class Program
{static void Main(){MyClass mc = new MyClass(); // Create instance.mc.PrintOut("object"); // Call method.}
}
16.4 接口是引用类型
接口是引用类型
不能通过类对象的成员访问接口,只能通过将类对象引用强制转换为接口类型来获取接口引用。
有了接口引用,可以使用点语法来调用接口的成员。
注意:接口引用不能调用类中不属于接口的其他成员。
interface IIfc1
{void PrintOut(string s);
}class MyClass : IIfc1
{public void PrintOut(string s){Console.WriteLine($"Calling through: { s }");}
}class Program
{static void Main(){MyClass mc = new MyClass(); // Create class object.// Call class object implementation method.mc.PrintOut("object"); // Cast class object ref to interface ref. IIfc1 ifc = (IIfc1)mc; ifc.PrintOut("interface"); // Call interface method.}
}// outputCalling through: objectCalling through: interface
16.5 接口和 as 运算符
将类对象引用强制转换为类未实现的接口引用,则会抛出异常。
使用 as 运算符可以避免抛出异常:
- 如果实现了类接口,则 as 返回指向接口的引用。
- 如果未实现接口,则 as 返回 null,而不抛出异常。
// Acts like cast: (ILiveBirth)a
ILiveBirth b = a as ILiveBirth; if (b != null)Console.WriteLine($"Baby is called: {b.BabyCalled() }");
16.6 实现多个接口
- 类或结构可以实现任意数量的接口
- 所有实现的接口必须列在基类列表中,以逗号分隔(在基类名称之后,如果有的话)。
// Declare interface.
interface IDataRetrieve { int GetData(); } // Declare interface.
interface IDataStore { void SetData( int x ); }// Interface Interface
class MyData: IDataRetrieve, IDataStore
{int Mem1; public int GetData() { return Mem1; }public void SetData( int x ) { Mem1 = x; }
}class Program
{static void Main() {MyData data = new MyData();data.SetData( 5 );Console.WriteLine($"Value = {data.GetData() }");}
}// output
Value = 5
16.7 实现具有重复成员的接口
由于类可以实现任意数量的接口,有可能两个或多个接口成员具有相同的签名和返回类型,
如果一个类实现了多个接口,并且其中一些接口具有相同的成员,则类可以实现单个成员来同时匹配所有重复成员的接口。
类中用一个成员来匹配多个接口中的重复成员
interface IIfc1
{void PrintOut(string s);
}
interface IIfc2
{void PrintOut(string t);
}// Implement both interfaces.
class MyClass : IIfc1, IIfc2
{// Single implementation for bothpublic void PrintOut(string s) {Console.WriteLine($"Calling through: { s }");}
}class Program
{static void Main(){MyClass mc = new MyClass();mc.PrintOut("object");}
}// outputCalling through: object
16.8 多个接口的引用
如果类实现了多个接口,可以获取每个接口的独立引用
interface IIfc1
{void PrintOut(string s);
}
interface IIfc2
{void PrintOut(string t);
}// Implement both interfaces.
class MyClass : IIfc1, IIfc2
{// Single implementation for bothpublic void PrintOut(string s) {Console.WriteLine($"Calling through: { s }");}
}class Program
{static void Main(){MyClass mc = new MyClass();// 获取接口的引用IIfc1 ifc1 = (IIfc1) mc; IIfc2 ifc2 = (IIfc2) mc; // 从类对象调用mc.PrintOut("object"); // 从接口调用ifc1.PrintOut("interface 1"); ifc2.PrintOut("interface 2"); }
}// outputCalling through: objectCalling through: interface 1Calling through: interface 2
16.9 派生成员作为实现
实现接口的类可以从基类继承实现的代码。
interface IIfc1 { void PrintOut(string s); }class MyBaseClass
{public void PrintOut(string s) {Console.WriteLine($"Calling through: { s }");}}class Derived : MyBaseClass, IIfc1
{
}class Program {static void Main(){Derived d = new Derived(); d.PrintOut("object."); }
}
16.10 显示接口成员实现
可以创建显示接口成员以实现相同接口的分离,
显示接口成员实现,主要特性:
位于实现了接口的类或结构中,
使用限定接口名称声明,由接口名称、成员函数、点分隔符组成
限定接口名称的声明如下:
// 声明接口
interface IIfc1 { void PrintOut(string s); }
interface IIfc2 { void PrintOut(string t); }class MyClass : IIfc1, IIfc2
{// 限定接口名称void IIfc1.PrintOut(string s) { Console.WriteLine($"IIfc1: {s }");}// Qualified interface namevoid IIfc2.PrintOut(string s) { Console.WriteLine($"IIfc2: {s }");}
}class Program
{static void Main(){MyClass mc = new MyClass();IIfc1 ifc1 = (IIfc1) mc; ifc1.PrintOut("interface 1");IIfc2 ifc2 = (IIfc2) mc; ifc2.PrintOut("interface 2"); }
}// output
IIfc1: interface 1
IIfc2: interface 2
如果有显式接口成员实现,类级别实现是允许的,不是必须的,
因此,对于成员函数而言,有如下三种实现方法:
1.类级别实现(不依赖接口,专门提供给类对象调用)。
2.显示接口成员实现(依赖接口,专门提供给接口调用)。
3.类级别和显示接口成员实现(类和接口都能调用)。
显示接口成员实现只能通过接口来访问:
class MyClass : IIfc1
{// 显式接口实现void IIfc1.PrintOut(string s) {Console.WriteLine("IIfc1");}public void Method1(){// 编译错误PrintOut("..."); // 编译错误this.PrintOut("...");// 转换为接口引用((IIfc1)this).PrintOut("..."); // OK, call method.}
}
16.11 接口可以继承接口
类在基类列表中只能有一个类名;而接口可以有任意多个接口。
interface IDataI0 : IDataRetrieve, IDataStore
{// ...
}
- 列表中的接口本身也可以继承其他接口。
- 继承后的接口包含自己的成员和所有继承接口的成员。
using System;// 定义数据检索接口
interface IDataRetrieve {int GetData();
}// 定义数据存储接口
interface IDataStore {void SetData(int value);
}// 组合接口
interface IDataIO : IDataRetrieve, IDataStore {}// 具体实现类
class MyData : IDataIO {private int nPrivateData;public int GetData() {return nPrivateData;}public void SetData(int value) {nPrivateData = value;}
}class Program {static void Main(string[] args) {MyData data = new MyData();data.SetData(5);Console.WriteLine("当前数据:{0}" + data.GetData());}
}
16.12 不同类实现一个接口的示例
interface ILiveBirth
{string BabyCalled();
}class Animal { } class Cat : Animal, ILiveBirth
{string ILiveBirth.BabyCalled(){ return "kitten"; }
}class Dog : Animal, ILiveBirth
{string ILiveBirth.BabyCalled(){ return "puppy"; }
}class Bird : Animal
{
}class Program
{static void Main(){Animal[] animalArray = new Animal[3]; // Create Animal array.animalArray[0] = new Cat(); // Insert Cat class object.// Insert Bird class object.animalArray[1] = new Bird(); // Insert Dog class object.animalArray[2] = new Dog(); // Cycle through array.foreach( Animal a in animalArray ) {ILiveBirth b = a as ILiveBirth; // if implements ILiveBirth...if (b != null)Console.WriteLine($"Baby is called: { b.BabyCalled() }");}}
}// output
Baby is called: kitten
Baby is called: puppy