第16章 接口 笔记 25.06.22
16.1 什么是接口
接口是声明一组函数成员,而不进行实现的引用类型,只能用类和结构来实现接口。
一个函数内不能传入多个类的实例做为参数,可以通过接口来实现。
csharp
// 声明接口
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);
}
}
// output
Name: John Doe, Age 35
Name: Jane Doe, Age 33
使用 IComparable 接口的示例
整数数组排序
c#
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接口 示例
c#
public interface IComparable
{
int CompareTo( object obj );
}
调用 CompareTo 方法时,应该返回如下值:
-
负数:当前对象 < 参数对象。
-
正数:当前对象 > 参数对象。
-
0:两个对象相等。
为自定义类实现 IComparable 接口后,就可以使用 Array.Sort 方法进行排序
要实现一个接口,类或结构必须做两件事:
在基类类别中列出接口的名称
为接口的每一个成员提供实现
c#
// 类实现接口
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 开始。
可以声明分部接口。
c#
interface IMyInterface1
{
//Semicolon in place of body
int DoStuff ( int nVar1, long lVar2 );
double DoOtherStuff( string s, long x );
}
接口和接口成员的访问性有一些不同:
接口声明可以有任何的访问修饰符。
接口成员是隐式 public,不能有任何访问修饰符。
c#
// 访问修饰符 可以用:public、protected、private、internal
public interface IMyInterface2
{
// 错误
private int Method1( int nVar1, long lVar2 );
}
16.3 实现接口
要实现接口,类和结构必须:
- 在基类列表中包含接口名称。
- 为每一个接口成员提供实现。
c#
class MyClass: IMyInterface1
{
int DoStuff ( int nVar1, long lVar2 )
{
// ...
}
double DoOtherStuff( string s, long x )
{
// ...
}
}
重要事项如下:
- 必须实现接口的所有成员。
- 基类名称必须放在所有接口之前。
c#
// 基类名、接口名
class Derived : MyBaseClass, IIfc1, IEnumerable, IComparable
{
...
}
简单接口的示例
c#
// Declare interface.
interface IIfc1
{
// Semicolon in place of body
void 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 接口是引用类型
接口是引用类型
不能通过类对象的成员访问接口,只能通过将类对象引用强制转换为接口类型来获取接口引用。
有了接口引用,可以使用点语法来调用接口的成员。
注意:接口引用不能调用类中不属于接口的其他成员。
c#
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.
}
}
// output
Calling through: object
Calling through: interface
16.5 接口和 as 运算符
将类对象引用强制转换为类未实现的接口引用,则会抛出异常。
使用 as 运算符可以避免抛出异常:
- 如果实现了类接口,则 as 返回指向接口的引用。
- 如果未实现接口,则 as 返回 null,而不抛出异常。
c#
// Acts like cast: (ILiveBirth)a
ILiveBirth b = a as ILiveBirth;
if (b != null)
Console.WriteLine($"Baby is called: {b.BabyCalled() }");
16.6 实现多个接口
- 类或结构可以实现任意数量的接口
- 所有实现的接口必须列在基类列表中,以逗号分隔(在基类名称之后,如果有的话)。
c#
// 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 实现具有重复成员的接口
由于类可以实现任意数量的接口,有可能两个或多个接口成员具有相同的签名和返回类型,
如果一个类实现了多个接口,并且其中一些接口具有相同的成员,则类可以实现单个成员来同时匹配所有重复成员的接口。
类中用一个成员来匹配多个接口中的重复成员
c#
interface IIfc1
{
void PrintOut(string s);
}
interface IIfc2
{
void PrintOut(string t);
}
// Implement both interfaces.
class MyClass : IIfc1, IIfc2
{
// Single implementation for both
public void PrintOut(string s)
{
Console.WriteLine($"Calling through: { s }");
}
}
class Program
{
static void Main()
{
MyClass mc = new MyClass();
mc.PrintOut("object");
}
}
// output
Calling through: object
16.8 多个接口的引用
如果类实现了多个接口,可以获取每个接口的独立引用
c#
interface IIfc1
{
void PrintOut(string s);
}
interface IIfc2
{
void PrintOut(string t);
}
// Implement both interfaces.
class MyClass : IIfc1, IIfc2
{
// Single implementation for both
public 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");
}
}
// output
Calling through: object
Calling through: interface 1
Calling through: interface 2
16.9 派生成员作为实现
实现接口的类可以从基类继承实现的代码。
c#
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 显示接口成员实现
可以创建显示接口成员以实现相同接口的分离,
显示接口成员实现,主要特性:
位于实现了接口的类或结构中,
使用限定接口名称声明,由接口名称、成员函数、点分隔符组成
限定接口名称的声明如下:
c#
// 声明接口
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 name
void 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.类级别和显示接口成员实现(类和接口都能调用)。
显示接口成员实现只能通过接口来访问:
c#
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 接口可以继承接口
类在基类列表中只能有一个类名;而接口可以有任意多个接口。
c#
interface IDataI0 : IDataRetrieve, IDataStore
{
// ...
}
- 列表中的接口本身也可以继承其他接口。
- 继承后的接口包含自己的成员和所有继承接口的成员。
c#
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 不同类实现一个接口的示例
c#
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