什么是接口
接口:一组函数成员而未实现的引用类型。只有类和结构能实现接口。
使用接口示例。
interface IInfo { string GetName(); string GetAge(); }
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 PersonAge;
public string GetName() { return First + " " + Last; }
public string GetAge() { return PersonAge.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", PersonAge = 33 }; PrintInfo(a); PrintInfo(b); } }
|
输出结果:
IComparable接口
假设有如下一段代码,它使用了Array类的一个静态方法Sort对一个未排序的int类型数组进行排序,并输出排序后的结果。
using System;
class Program { static void Main() { var myInt = new[] { 20, 4, 16, 9, 2 };
Array.Sort( myInt );
foreach ( var i in myInt ) { Console.Write( "{0} ", i ); } } }
|
Sort方法在int数组上工作得很好,但是如果我们尝试在自己的类上使用就会发生异常,例如下面的MyClass类。
class MyClass { public int TheValue; }
|
Sort不能对MyClass对象数组进行排序,原因在于:它不知道如何比较自定义对象及如何进行排序。Array类的Sort方法其实依赖于一个叫做IComparable的接口,它声明在BCL中,包含唯一的CompareTo方法。CompareTo方法接收一个object类型的参数,可以匹配任何引用类型。
public interface IComparable { int CompareTo(object obj); }
|
Sort使用的算法依赖于元素的CompareTo方法来决定两个元素的次序。int类型实现了IComparable,但是MyClass没有。因此,想要用Sort对MyClass类型进行排序,就要让MyClass类实现IComParable接口。
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; } }
|
现在MyClass实现了IComparable接口,可以使用Sort来进行排序了。
class Program { static void PrintOut(string s, MyClass[] mc) { Console.Write(s); foreach (var m in mc) { Console.Write("{0} ", m.TheValue); } Console.WriteLine(""); } static void Main() { var myInt = new[] { 20, 4, 16, 9, 2 }; MyClass[] mcArr = new MyClass[5]; 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); } }
|
输出结果:
声明接口
接口声明不能包含以下成员:数据成员、静态成员。
接口声明只能包含如下非静态成员函数的声明:方法、属性、事件、索引器。
接口声明可以有任何的访问修饰符。然而,接口的成员是隐式public的,不允许有任何访问修饰符,包括public。
interface IMyInterface1 { int DoStuff(int nVar1, long lVar2); double DoOtherStudd(string s, long x); }
|
实现接口
只有类和结构才能实现接口。
如果类实现了接口,它必须实现接口的所有成员。
如果类从基类继承并实现了接口,那么基类列表中的基类名称必须放在所有接口之前(只能继承一个基类,可以实现多个接口)。
class Derived : MyBaseClass, IIfc1, IEnumerable, IComparable // 基类必须在最前面 {
}
|
简单接口的示例
interface IIfc1 { void PrintOut(string s); }
class MyClass : IIfc1 { public void PrintOut(string s) { Console.WriteLine("Calling through: {0}", s); } }
class Program { static void Main() { MyClass mc = new MyClass(); mc.PrintOut("object"); } }
|
输出结果:
接口是引用类型
接口是一种引用类型。
我们不能直接通过类对象的成员访问接口。然而,我们可以通过把类对象引用强制转换为接口类型来获取指向接口的引用。一旦有了接口的引用,我们就可以使用点号来调用接口的方法。
IIfc1 ifc = (IIfc1)mc; ifc.PrintOut("interface");
|
接口和as运算符
如果我们尝试将类对象引用强制转换为未实现的接口的引用,强制转换操作就会抛出异常。
这时可以用as运算符来避免异常:如果类实现了接口,表达式返回指向接口的引用。如果类没有实现接口,表达式返回null而不是抛出异常。
ILiveBirth b = a as ILiveBirth; if (b != null) { Console.WriteLine("Baby is called: {0}", b.BabyCalled()); }
|
实现多个接口
类或结构可以实现任意数量的接口。所有实现的接口必须列在基类列表中,并以逗号分隔。
interface IDataRetrieve { int GetData(); }
interface IDataStore { void SetData(int x); }
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 = {0}", data.GetData()); } }
|
实现具有重复成员的接口
如果一个类实现了多个接口,并且其中一些接口有相同签名和返回类型的成员,那么类可以实现单个成员来满足所有包含重复成员的接口。
interface IIfc1 { void PrintOut(string s); }
interface IIfc2 { void PrintOut(string t); }
class MyClass : IIfc1, IIfc2 // 实现两个接口 { public void PrintOut(string s) { Console.WriteLine("Calling through: {0}", s); } }
class Program { static void Main() { MyClass mc = new MyClass(); mc.PrintOut("object"); } }
|
多个接口的引用
如果类实现了多个接口,我们可以获取每一个接口的独立引用。
interface IIfc1 { void PrintOut(string s); }
interface IIfc2 { void PrintOut(string s); }
class MyClass : IIfc1, IIfc2 { public void PrintOut(string s) { Console.WriteLine("Calling through: {0}", s); } }
class Program { static void Main() { MyClass mc = new MyClass(); IIfc1 ifc1 = (IIfc1)mc; IIfc2 ifc2 = (IIfc2)mc; mc.PrintOut("object"); ifc1.PrintOut("interface1"); ifc2.PrintOut("interface2"); } }
|
输出结果:
派生成员作为实现
实现接口的类可以从它的基类继承实现的代码。
interface IIfc1 { void PrintOut(string s); }
class MyBaseClass { public void PrintOut(string s) { Console.WriteLine("Calling through: {0}", s); } }
class Derived : MyBaseClass, IIfc1 { }
class Program { static void Main() { Derived d = new Derived(); d.PrintOut("object"); } }
|
显式接口成员实现
可以使用接口名称.成员名称的方式显式实现接口。
class MyClass : IIfc1, IIfc2 { void IIfc1.PrintOut(string s) { } void IIfc2.PrintOut(string s) { } }
|
MyClass为两个接口的成员声明了显式接口成员实现。
interface IIfc1 { void PrintOut(string s); }
interface IIfc2 { void PrintOut(string t); }
class MyClass : IIfc1, IIfc2 { void IIfc1.PrintOut(string s) { Console.WriteLine("IIfc1: {0}", s); }
void IIfc2.PrintOut(string t) { Console.WriteLine("IIfc1: {0}", t); } }
class Program { static void Main() { MyClass mc = new MyClass(); IIfc1 ifc1 = (IIfc1)mc; ifc1.PrintOut("interface 1"); IIfc2 ifc2 = (IIfc2)mc; iifc2.PrintOut("interface 2");
} }
|
注意:
- 表示显式接口成员实现的方框不是灰色的,因为它们表示实际的代码。
- 在图中接口方法没有指向类级别实现,而是包含了自己的代码。
- 显式接口成员实现,接口名前面不能加public,因为是接口中的成员,默认是public。
- 如果有显式接口成员实现,类级别的实现是允许的,但不是必须的。
三种实现场景:
- 类级别实现
- 显式接口成员实现
- 类级别和显式接口成员实现
访问显式接口成员实现
显式接口成员实现只可以通过指向接口的引用来访问。也就是说,其他的类成员都不可以直接访问它们。
class MyClass : IIfc1 { void IIfc1.PrintOut(string s) { Console.WriteLine("IIfc1"); }
public void Method1() { PrintOut("..."); this.PrintOut("..."); ((IIfc1)this).PrintOut("..."); } }
|
这个限制对继承产生了重要的影响。由于其他类不能直接访问显式接口成员实现,衍生类的成员也不能直接访问它们。它们必须总是通过接口的引用来访问。
接口可以继承接口
接口可以从一个或多个接口继承。
interface IDataRetrieve { int GetData(); }
interface IDataStore { void SetData(int x); }
interface IDataIO : IDataRetrieve, IDataStore {
}
class MyData : IDataIO { int nPrivateData; public int GetData() { return nPrivateData; }
public void SetData(int x) { nPrivateData = x; } }
class Program { static void Main() { MyData data = new MyData(); data.SetData(5); Console.WriteLine("{0}", data.GetData()); } }
|
不同类实现一个接口的示例
interface ILiveBirth { string BabyCalled(); }
class Animal { }
class Cat : Animal, ILiveBirth // 声明Cat类 { string ILiveBirth.BabyCalled() { return "kitten"; } }
class Dog : Animal, ILiveBirth //声明Dog类 { string ILiveBirth.BabyCalled() { return "puppy"; } }
class Bird : Animal { }
class Program { static void Main() { Animal[] animalArray = new Animal[3]; animalArray[0] = new Cat(); animalArray[1] = new Bird(); animalArray[2] = new Dog(); foreach (Animal a in animalArray) { ILiveBirth b = a as ILiveBirth; if (b != null) { Console.WriteLine("Baby is called: {0}", b.BabyCalled()); } }
} }
|