什么是接口
接口与class类似,但是它只为其成员提供了规格,而没有提供具体的实现
接口的成员都是隐式抽象的(没有实现)
一个class或者struct可以实现多个接口
接口的例子
接口的命名通常以大写的“I”为开头(约定)
public interface IEnumerator
{
bool MoveNext();
object Current{get;}
void Reset();
}
接口的实现
接口的成员都是隐式public,不可以声明访问修饰符
实现接口对它的所有成员进行public的实现:
internal class Countdown:IEnumerator
{
int count = 11;
public bool MoveNext() => count-- > 0;
public object Current => count;
public void Reset(){throw new NotSupportedException();}
}
对象与接口的转换
可以隐式的把一个对象转化成它实现的接口:
IEnumator e = new Countdown();
while(e.MoveNext())
Console.Write(e.Current); // 109876543210
虽然Countdown是一个internal的class,但是可以通过把它的实例转化成IEnumerator接口来公共的访问它的成员。
接口的扩展
接口可以继承其它接口
public interface IUndoable { void Undo();}
public interface IRedoable:IUndoable { void Redo();}
//IRedoable继承了IUndoable的所有成员
显式的接口实现
实现多个接口的时候可能会造成成员签名的冲突。通过显式实现接口成员可以解决这个问题。
interface T1 {void Foo();}
interface T2 {int Foo();}
public class Widget:T1,T2
{
public void Foo(){}
public void T2.Foo(){}
}
本例中,想要调用相应实现的接口方法,只能把其实例转化成相应的接口才行:
Widget w = new Widget();
w.Foo();
((T1)w).Foo();
((T2)w).Foo();
另一个显式实现接口成员的理由是故意隐藏那些对于类型来说不常用的成员。
VIRTUAL的实现接口成员
隐式实现的接口成员默认是sealed的。(无法重写的,隐式实现意思是直接实现,没有下面各种修饰符)
如果想要进行重写的话,必须在基类中把成员标记为virtual或者abstract
public interface IUndoable(){void Undo();}
public class TextBox:IUndoable
{
public virtual void
Undo()=> Console.WriteLine("TextBox.Undo");
}
public class RichTextBox:TextBox
{
public override void
Undo()=>Console.WriteLine("RichTextBox.Undo");
}
显式实现的接口成员不可以被标记为virtual,也不可以通过寻常的方式来重写,但是可以对其进行重新实现。
在子类中重新实现接口
子类可以重新实现父类已经实现的接口成员
重新实现会“劫持”成员的实现(通过转化为接口然后调用),无论在基类中该成员是否使virtual得。无论该成员是显示还隐式的实现(但最好还是显式实现的)。
public interface IUndoable{ void Undo();}
public class TextBox:IUndoable
{
void IUndoable.Undo()=>CW("TextBox.Undo");
}
public class RishTextBox:TextBox,IUndoable
{
public void Undo()=>CW("RichTextBox.Undo");
}
如果TextBox是隐式实现的Undo
public class TextBox:IUndoable
{
public void Undo()=>CW("TextBox.Undo");
}
那么:
RichTextBox r = new RichTextBox();
r.Undo();
//RichTextBox.Undo
((IUndoable)r).Undo();
//TextBox.Undo
((TextBox)r).Undo();
//TextBox.Undo
说明重新实现接口何种劫持只对转化为接口后的调用其作用,对转化为基类后的调用不起作用。
重新实现适用于重写显式实现的接口成员。
重新实现接口的替代方案
即使是显示实现的接口,接口的重新实现也可能有一些问题:
子类无法调用基类的方法
基类的开发人员没有预见到方法会被重新实现,并且可能不允许潜在的后果
最好的办法是设计一个无需重新实现的基类:
隐式实现成员的时候,按需标记virtual
显式实现成员的时候,可以这样做:
public class TextBox:IUndoable
{
void IUndoable.Undo()=>Undo();
protected virtual Undo()=>CW("TextBox.Undo");
}
public class RichTextBox:TextBox
{
protected override void Undo()=>CW("RichTextBox.Undo");
}
接口与装箱
把struct转化为接口会导致装箱
调用struct上隐式实现的成员不会导致装箱
interface I{void Foo();}
struct S:I{public void Foo()}
..
S s = new S();
s.Foo(); //No boxing.
I i = s; //Box
i.Foo();