I+名词
接口是由抽象类演变而来的。
抽象类是未完全实现逻辑的类,其内部可以有抽象成员,也可以有非抽象成员;且子类在覆写抽象成员时,需要修饰符override。
而接口是完全未实现逻辑的,其内部只允许存在抽象成员而不能包含非抽象成员,即不能包含数据成员和静态成员;且成员是隐式默认public和absolute的,不允许显式地写出来;子类在实现接口的方法时,不需要修饰符override。
接口声明只能包含如下类型的非静态成员函数的声明:
方法、属性、事件、索引器
只有类和结构才能实现接口,在实现时应满足:
一个类可以同时继承一个基类和多个接口,不过在基类列表中基类名称应放置在接口名称之前。
如果在一个类实现的多个接口中,存在两个或多个接口的成员具有相同的签名和返回值类型,那么类可以实现单个成员来满足所有包含重复成员的接口。
让一个方法可以接受各种类型的对象,并且不管该类有什么样的结构。
比如,存在一个方法PrintIHuman(),如果要让其接受Student和Programmer这两个不同的类的对象,就需要让这两个类继承同一个接口:
namespace TestConsole
{
//声明接口
interface IHuman
{
void Action();
}
//声明Student类,其需要实现接口IHuman的方法Action()
class Student : IHuman
{
public void Action()
{
Console.WriteLine("The student are studying...");
}
}
//声明Programmer类,其与Student类有着不同的结构,实现Action()方法的方式也不相同
class Programmer : IHuman
{
public String Name { get; set; }
public void Action()
{
Console.WriteLine(Name + " is writing code...");
}
}
//测试:
class Program
{
static void PrintIHuman(IHuman ih)
{
ih.Action();
}
static void Main(string[] args)
{
Student stu = new Student();
PrintIHuman(stu);
Programmer prom = new Programmer();
prom.Name = "Ivan";
PrintIHuman(prom);
}
}
}
运行结果:
The student are studying…
Ivan is writing code…
我们可以这样理解:接口就是一种契约,有些方法只接收遵循某种契约的参数,以上面的代码为例,IHuman接口是一种契约,而PrintIHuman()这个方法只接收遵循IHuman这个契约的参数,当Student和Programmer这两个类都遵循IHuman这个契约时,就可以使用PrintIHuman()方法了。
可以认为,接口是为解耦而生的。例如,当一个方法需要某种契约的参数时,而此时遵守这个契约的类出了bug,在修复期间,方法就可以先去使用其他遵守这个契约的类;如果没有接口的话,这个方法和这个类之间的耦合度就很大了,当类出了问题,方法也会随之瘫痪,加大了维护难度和成本。
比如Array的sort方法,能对数组进行排序,但如果是我们自己写的类,想让sort方法对我们的类对象根据成员值大小进行排序,那sort是做不到的,除非我们的类实现了 IComparable 接口。
namespace TestConsole
{
//实现接口:IComparable,要实现其方法:CompareTo
class MyTest : IComparable
{
public int MyVar { get; set; }
public int CompareTo(object obj)
{
MyTest mt = (MyTest)obj;
if (this.MyVar < mt.MyVar) return -1;
if (this.MyVar > mt.MyVar) return 1;
return 0;
}
}
class Program
{
//打印数组元素
static void printArray(MyTest[] mt)
{
foreach (var item in mt)
{
Console.Write(item.MyVar+" ");
}
Console.WriteLine("");
}
//测试
static void Main(string[] args)
{
var myInt = new[] { 20, 4, 47, 6, 1 };
MyTest[] amt = new MyTest[5];
for (int i = 0; i < 5; i++)
{
amt[i] = new MyTest();
amt[i].MyVar = myInt[i];
}
Console.WriteLine("排序前:");
printArray(amt);
Array.Sort(amt);
Console.WriteLine("排序后:");
printArray(amt);
}
}
}
运行结果:
排序前:
20 4 47 6 1
排序后:
1 4 6 20 47
接口是引用类型,可以通过强制转换运算符来获取对象接口的引用,但这种操作会抛出异常,我们可以使用as运算符来解决这一问题。
is运算符则用来判断某个对象是否为某个类型。
Student stu = new Student(); //“Student”是实现了接口“IHuman”的类
IHuman ih = stu as IHuman; //类型转换
if (ih is null) //判断对象ih是否为空
{
Console.WriteLine("ih is null");
}
if (ih is IHuman) //判断对象ih是否为IHuman类型
{
Console.WriteLine("ih is IHuman");
}
if (ih is Student) //判断对象ih是否为Student类型
{
Console.WriteLine("ih is Student");
}
运行结果:
ih is IHuman
ih is Student
如果一个类实现了多个接口,我们可以获取每一个接口的独立引用。
namespace TestConsole
{
interface IStone
{
String Color { get; set; }
}
interface IMonkey
{
void Action();
}
class StoneMonkey : IStone, IMonkey
{
public String Color { get; set; }
public void Action()
{
Console.WriteLine("I can subdue demons.");
}
}
//测试:
class Program
{
static void Main(string[] args)
{
StoneMonkey sunWuKong=new StoneMonkey();
IMonkey falseMonkey = sunWuKong as IMonkey;
//falseMonkey独立引用了sunWuKong的IMonkey接口
//falseMonkey可以使用Action()方法,但无法像sunWuKong一样使用Color属性
falseMonkey.Action();
sunWuKong.Color = "golden";
sunWuKong..Action();
}
}
}
前面提到过,单个类的单个方法可以满足多个接口的重复成员,但如果想要为每一个接口分离实现,则需要显式实现接口成员。
显示接口成员实现:使用限定接口名称来声明,由接口名称和成员名称以及它们中间的点分隔符号构成。此时public是隐式的,不能显式写出。
namespace TestConsole
{
interface ITest1
{
void Action();
}
interface ITest2
{
void Action();
}
class MyClass : ITest1, ITest2
{
void ITest1.Action()
{
Console.WriteLine("1号接口");
}
void ITest2.Action()
{
Console.WriteLine("2号接口");
}
}
class Program
{
static void Main(string[] args)
{
MyClass mc = new MyClass(); //MyClass对象mc无法直接调用Action方法
//可以通过类型转换来调用:
((ITest1)mc).Action();
//或者用接口类型创建的MyClass对象来调用:
ITest2 it2 = new MyClass();
it2.Action();
}
}
}
运行结果:
1号接口
2号接口
注意,以上的“Action()”不是类级别的方法,所以MyClass类的对象mc无法直接调用它。不过,存在显式接口成员实现时,类级别的实现是允许的,即MyClass类可以同时包含显式接口成员实现和类级别的实现:
class MyClass : ITest1, ITest2
{
//显式接口成员实现
void ITest1.Action()
{
Console.WriteLine("1号接口");
}
void ITest2.Action()
{
Console.WriteLine("2号接口");
}
//类级别的实现
public void Action()
{
Console.WriteLine("类级别的实现");
}
}
class Program
{
static void Main(string[] args)
{
MyClass mc = new MyClass();
mc.Action();
ITest1 it1 = new MyClass();
it1.Action();
ITest2 it2 = new MyClass();
it2.Action();
}
}
运行结果:
类级别的实现
1号接口
2号接口