美文网首页
C# 接口简介

C# 接口简介

作者: JeetChan | 来源:发表于2019-08-16 18:20 被阅读0次

声明

本文内容来自微软 MVP solenovex 的视频教程——真会C#?- 第3章 创建类型,大致和第 9 课—— 接口简介 对应。可在 GitHub 中查看 C# 视频教程的配套PPT

本文主要包括以下内容:

  1. 接口的扩展
  2. 显式的接口实现
  3. virtual 的实现接口成员
  4. 在子类中重新实现接口
  5. 接口与装箱

接口

接口与 class 类似,但是它只为其成员提供了规格,而没有提供具体的实现,接口的成员都是隐式抽象的。一个 class 或者 struct 可以实现多个接口。

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(); }
}

可以隐式的把一个对象转化成它实现的接口:

IEnumerator 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 I1 { void Foo(); }
interface I2 { int Foo(); }
public class Widget : I1, I2
{
    public void Foo()
    {
        Console.WriteLine ("Widget's implementation of I1.Foo");
    }
    int I2.Foo()
    {
        Console.WriteLine ("Widget's implementation of I2.Foo");
        return 42;
    }
}

本例中,想要调用相应实现的接口方法,只能把其实例转化成相应的接口才行:

Widget w = new Widget();
w.Foo(); // Widget's implementation of I1.Foo
((I1)w).Foo(); // Widget's implementation of I1.Foo
((I2)w).Foo(); // Widget's implementation of I2.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");
}

无论是转化为基类还是转化为接口来调用接口的成员,调用的都是子类的实现:

RichTextBox r = new RichTextBox();
r.Undo(); // RichTextBox.Undo
((IUndoable)r).Undo(); // RichTextBox.Undo
((TextBox)r).Undo(); // RichTextBox.Undo

显式实现的接口成员不可以被标记为 virtual,也不可以通过寻常的方式来重写,但是可以对其进行重新实现。

在子类中重新实现接口

子类可以重新实现父类已经实现的接口成员。重新实现会“劫持”成员的实现(通过转化为接口然后调用),无论在基类中该成员是否是 virtual 的。无论该成员是显式的还是隐式的实现(但最好还是显式实现的)。

public interface IUndoable { void Undo(); }
public class TextBox : IUndoable
{
    void IUndoable.Undo() => Console.WriteLine ("TextBox.Undo");
}
public class RichTextBox : TextBox, IUndoable
{
    public void Undo() => Console.WriteLine ("RichTextBox.Undo");
}

转化为接口后调用重新实现的成员,就是调用子类的实现:

RichTextBox r = new RichTextBox();
r.Undo(); // RichTextBox.Undo Case 1
((IUndoable)r).Undo(); // RichTextBox.Undo Case 2

如果 Textbox 是隐式实现的 Undo:

public class TextBox : IUndoable
{
    public void Undo() => Console.WriteLine ("TextBox.Undo");
}

那么:

RichTextBox r = new RichTextBox();
r.Undo(); // RichTextBox.Undo Case 1
((IUndoable)r).Undo(); // RichTextBox.Undo Case 2
((TextBox)r).Undo(); // TextBox.Undo Case 3

说明重新实现接口这种劫持只对转化为接口后的调用起作用,对转化为基类后的调用不起作用。重新实现适用于重写显式实现的接口成员。

重新实现接口的替代方案

即使是显式实现的接口,接口的重新实现也可能有一些问题:

  1. 子类无法调用基类的方法
  2. 基类的开发人员没有预见到方法会被重新实现,并且可能不允许潜在的后果

最好的办法是设计一个无需重新实现的基类:

  1. 隐式实现成员的时候,按需标记 virtual
  2. 显式实现成员的时候,可以这样做:
   public class TextBox : IUndoable
   {
    void IUndoable.Undo() => Undo(); // Calls method below
    protected virtual void Undo() => Console.WriteLine ("TextBox.Undo");
   }
   public class RichTextBox : TextBox
   {
    protected override void Undo() => Console.WriteLine("RichTextBox.Undo");
   }

如果不想有子类,那么直接把 class 给 sealed。

接口与装箱

把 struc t转化为接口会导致装箱,调用 struct 上隐式实现的成员不会导致装箱。

interface I { void Foo(); }
struct S : I { public void Foo() {} }
...
S s = new S();
s.Foo(); // No boxing.
I i = s; // Box occurs when casting to interface.
i.Foo();

参考

Interfaces (C# Programming Guide)
interface (C# Reference)

相关文章

  • C# 接口简介

    声明 本文内容来自微软 MVP solenovex 的视频教程——真会C#?- 第3章 创建类型,大致和第 9 课...

  • 游戏系统机器人

    mono C#项目简介 想必C#玩家往往受到Java玩家的嘲笑,不能写移动端。mono C#的目的正是通过C#的中...

  • 提供 CTP行情与交易SDK的C#接口

    提供 CTP行情与交易SDK的C#接口 QQ 516333132

  • 接口的作用

    C#接口是一个让很多初学C#者容易迷糊的东西,用起来好像很简单,定义接口,里面包含方法,但没有方法具体实现的代码,...

  • Lua 异常处理

    在写lua代码,特别是有热更新功能的项目中,c#中新加了接口之后,然后在lua中调用了此接口。但是项目中c#代码是...

  • C#接口

    继承接口后,我们需要实现接口的方法 MethodToImplement() , 方法名必须与接口定义的方法名一致

  • C#接口

    C# 接口中能否定义字段? 答案 是不能。 语法上不允许。 也不符合接口的目标。 接口可以看成是对实现的约束, 而...

  • C#接口

    参考: 接口: https://www.runoob.com/csharp/csharp-interface.ht...

  • c#接口

    接口与class类似,但是它只为其成员提供了规格,而没有提供具体的实现 接口的成员都是隐式抽象的 一个class或...

  • C#接口

    接口定义了所有类继承接口时应遵循的语法合同。接口定义了语法合同“是什么”部分,派生类定义了语法合同“怎么做”部分。...

网友评论

      本文标题:C# 接口简介

      本文链接:https://www.haomeiwen.com/subject/xqnqsctx.html