之前的学习中,学习了C#中的继承,但是C#并不支持多重继承,也就是说如果一个子类想要同时继承2个及以上基类的属性、方法,C#是不允许的。为了解决这个问题,C#用接口来“代替”多重继承。
接口:
接口是对一组方法声明的统一命名,但这些方法没有提供任何实现。也就是说,在接口中声明一组方法,但不提供实现,而继承该接口的类需要实现这些方法。比如说:一个“吃”接口中声明了很多方法:细嚼慢咽、狼吞虎咽、津津有味,但不能具体说明每种吃的方法是如何实现的。然后有一些类(小明、小王)继承了该“吃”接口,并要具体实现小明、小王的细嚼慢咽、狼吞虎咽、津津有味方法。
1.如何定义接口
接口的定义与类的定义类似,只是类使用class关键字,而接口使用interface关键字,如:public interface Eat. 然后,需要在接口中定义一些方法、属性等。
注意:
①接口中只能定义方法、属性、事件、索引器或者这4类成员的组合,不能定义字段、运算符重载、实例构造函数和析构函数;
②接口中定义方法时不能添加访问修饰符,因为接口中方法默认为public,且也不能显示的添加public;
③接口中的所有成员都默认是公共的,因此都不能再使用访问修饰符。
2.接口的继承
接口的继承与类的继承方法是一样的,定义完接口后,类使用“:”,后接接口名即可继承该接口。例如:
//定义接口
public interface Compare
{
//声明方法,但不实现
int CompareTo(object other)
{
}
}
//定义类,并继承接口
public classs Person : Compare
{
private int age;
public int Age
{
get{return age;}
set{age = value;}
}
//实现接口中的方法
public int CompareTo(object value)
{
//若value中没有值,则返回1,表示当前对象比传入对象大
if (value == null)
{
return 1;
}
//若value有值,则强制把object类型转换为Person类型
Person otherPerson = (Person)value;
//把当前对象的Age属性与需要比较的对象的Age属性进行比较
if (this.Age < otherPerson.Age)
{
return -1;
}
else if (this.Age > otherPerson.Age)
{
return 1;
}
else
return 0;
}
}
3.调用接口
在上面,我们定义了接口,并在类中继承了该接口,然后我们需要在Main中调用该接口,一种实现方法为先将类实例化,然后通过类实例“.”接口方法名(参数)来达到调用接口方法的目的。例如:
class Program
{
static void Main(string[] args)
{
//类的实例化
Person p1 = new Person();
Person p2 = new Person();
p1.Age = 20;
p2.Age = 30;
//调用接口方法,对p1和p2的年龄进行比较
if (p1.CompareTo(p2) > 0)
{
Console.WriteLine("p1比p2大");
}
else if (p1.CompareTo(p2) < 0)
{
Console.WriteLine("p1比p2小");
}
else
{
Console.WriteLine("p1和p2一样大");
}
Console.Read();
}
}
运行以上代码,结果为:
显式接口:
在上面的接口实现方法中,使用的是隐式的接口实现方法,即在实现代码中没有指定实现哪个接口中的CompareTo方法。但是如果有多个接口,且每个接口中都定义了一个相同返回类型、相同名字的方法,则使用隐式接口实现会出现问题。因为程序无法判定调用的是哪个接口中的这个方法。例如:
//中文接口
interface Chinese
{
//声明接口方法
void Hello();
}
//英文接口
interface English
{
//声明接口方法
void Hello();
}
//Greeting类实现了两个接口
public class Greeting : Chinese, English
{
//隐式接口实现
public void Hello()
{
Console.WriteLine("你好");
}
}
//隐式接口的实现
class Program
{
static void Main(string[] args)
{
Greeting greeting = new Greeting();
//调用中文接口
Chinese ChineseGreeting = (Chinese)greeting;
greeting.Hello();
//调用英文接口
English EnglishGreeting = (English)greeting;
greeting.Hello();
Console.Read();
}
}
运行以上代码,结果为:
可以看到,不管调用的是哪个接口,程序调用的都是同一个方法实现,输出都为“你好”。
为了解决这个问题,我们需要使用显式接口来解决这种情况。显式接口的实现方法为:在类的方法中指明实现的是哪个接口的方法,并在调用接口时修改为接口实例调用即可。例如:
将以上代码的Greeting类实现修改为:
public class Greeting : Chinese, English
{
//显式接口实现
void Chinese.Hello()
{
Console.WriteLine("你好");
}
void English.Hello();
{
Console.WriteLine("Hello");
}
}
调用接口部分修改为:
ChineseGreeting.Hello();
EnglishGreeting.Hello();
重新运行修改后的代码,结果为:
可以看到,显式接口实现解决了命名冲突的问题。
注意:
①使用显式接口时,类方法实现中不能添加任何访问修饰符,因为成员默认为私有;
②使用显式接口时,因为成员默认为私有,不能通过类的对象进行访问,必须通过接口实例进行访问。
前面具体分析了隐式与显式接口实现方式两种情况,但何时需要使用哪种接口需要自行判断,判断方法为:
ⅰ.采用隐式接口实现时,类和接口都可以访问接口中的方法;而若采用显式接口实现方式,接口方法只能通过接口来完成访问,因为此时接口方法默认为私有。
ⅱ.当类实现单个接口时,通常使用隐式接口实现方式,这样类的对象可以直接去访问接口方法。
ⅲ.当类实现了多个接口,并且接口中包含相同的方法名称、参数和返回类型时,则应使用显式接口实现方式。即使没有相同的方法签名,在实现多个接口时,仍推荐使用显式的方式,因为这样可以标识出哪个方法属于哪个接口。