c#基础系列之C# 接口(interface)
接口定义
Public interface IAnimals{
void eat();
}
tips:接口命名以“I”开头
接口只提供给类定义,但不提供其实现,与类有所不同。
- 接口中的成员只能是隐含抽象的,而类包含抽象与具体实现的成员
- 一个类可以实现多个接口,但只能继承一个类
- 接口成员总是隐式定义为Public,不可以用其他访问修饰符声明,实现接口意味着为其他成员提供所有public实现
接口用法
- 可以把对象隐式或显式转换为他实现的任意一个接口
- 如果继承接口的是被 internal 修饰的类,那么可以将类的实例转换成接口类型,那么其内部实现的接口成员可以作为public成员访问
- 继承并实现接口方法的类,在其主函数中不能直接使用已实现的接口的成员方法,需要定义该接口类型的变量然后获取继承自该接口的类的实例,通过该变量调用已经实现的方法。
- 接口中不能定义字段,但可以定义属性!!!!
public interface IEnumeratora
{
bool MoveNext();
object Current { get; }
void reset();
}
internal class Countdown : IEnumeratora
{
int count = 11;
public bool MoveNext()
{
return count-- > 0;
}
public object Current
{
get
{
return count;
}
}
public void reset()
{
count = 11;
}
}
static void Main(string[] args)
{
IEnumeratora e = new Countdown();
do
{
Console.WriteLine(e.Current);
} while (e.MoveNext());
Console.ReadKey();
}
结果
扩展接口
接口可以从及其他接口派生
public interface A{
void a();
}
public interface B:A{
void b();
}
- B继承A中的所有成员,实现B必须实现A中的成员
显式与隐式接口实现
- 什么是显式与隐式实现接口
public interface IAnimals{
void eat()
}
//这就是显式
public class Bird :IAnimals{
void IAnimals.eat(){
Console.WriteLine("Bird.eat");
}
}
//这就是隐式
public class Bird1 :IAnimals{
public void eat(){
Console.WriteLine("Bird1.eat");
}
}
tips:隐式实现接口需要用public等修饰符修饰
- 当实现多个接口时,如果接口成员标识符出现重复那么,便可以用显式实现接口的方式来避免错误
- 需要隐藏和正常类使用差别很大的或是干扰性特别强的成员时,需要以显式实现的的方式来避免错误
public interface IAnimals{
void eat()
}
public interface IPerson{
void eat()
}
public class people : IPerson ,IAnimals{
void IAnimals.eat(){
Console.WriteLine("IAnimals.eat");
}
void IPerson .eat(){
Console.WriteLine("IPerson .eat");
}
}
- 调用显式实现的接口的唯一办法是先将类的实例转换为相应的接口
People peo = new People();
((IPerson)peo).eat();
- 在默认情况下接口成员是隐式实现的,需要使用virtual或是abstract来达到在派生类中重写该成员方法
public interface IPerson{
void eat();
}
public class People : IPerson{
public virtual void eat(){
Console.WriteLine("People.eat");
}
}
public class Zach : People{
public override void eat(){
Console.WriteLine("Zach.eat");
}
}
Zach zachy = new Zach();
zachy.eat(); //Zach.eat
((IPerson)zachy).eat(); //Zach.eat
((People)zachy).eat(); //Zach.eat
- 不管是从接口还是基类中调用的成员方法,调用的都是子类的实现
- 显式实现借口成员并不能标识为virtual,所以 显式实现接口的基类 并不能被 继承自它的派生类 重写,但却可以重新实现
在子类重新实现接口
- 子类可以重新实现基类已经实现的任一接口,并且可以屏蔽基类中实现的成员方法
public interface IAnimals{ void eat(); }
public class Animals : IAnimals{
public void IAnimals.eat(){
Console.WriteLine("Animals.eat");
}
}
public class Bird : Animals,IAnimals{
public new void eat(){
Console.WriteLine("Bird.eat");
}
}
-从接口中调用成员时,调用的是子类的实现
Bird b = new Bird();
b.eat(); //Bird.eat
((IAnimals)b).eat(); //Bird.eat
-假定Bird类不变,将Animals类中的显式实现改成隐式实现,那么会变成这样
Bird b = new Bird();
b.eat(); //Bird.eat
((IAnimals)b).eat(); //Bird.eat
((Animals)b).eat() //Animals.eat
- 即使在显式实现中,接口依然会出现问题
- 子类无法调用基类的方法
- 基类无法判断子类是否需要重新实现基类方法
- 于是可以使用其他方式替代子类重新实现
接口的替代重新实现的方式
- 当隐式实现接口成员时,可以的话将其标注为virtual
- 如果可以预测子类需要重写某些逻辑,可以用这种方法去完成重新实现的功能‘
public class Animals : IAnimals{
void IAnimals.eat(){ eat();}
public virtual void eat(){
Console.WriteLine("Animals.eat");
}
}
public class Bird : Animals{
publicoverride void eat(){
Console.WriteLine("Bird .eat");
}
}
类和接口的对比(什么情况下写类,什么情况下写接口)
- 当能够自然的共享实现时,使用类和子类
当实现是独立的时候,使用接口
- 举个栗子
所有的鸟都可以共享鸟的这一个标签,但所有的鸟并不能共享食肉动物这个标签
所有的虫子可以共享虫这一个标签,但并不能共享食肉这个标签
食肉是鸟或虫子里部分个体的标签,就说食肉是独立的
而,鸟/虫子是共享的