介面基礎知識

介面的功能稱為功能契約。這意味著它宣告瞭屬性和方法,但它沒有實現它們。

所以不像類介面:

  • 無法例項化
  • 不能有任何功能
  • 只能包含方法* (屬性和事件是內部方法)
  • 繼承介面稱為實現
  • 你可以從 1 個類繼承,但可以實現多個介面
public interface ICanDoThis{
    void TheThingICanDo();
    int SomeValueProperty { get; set; }
}

需要注意的事項:

  • I 字首是用於介面的命名約定。
  • 函式體替換為分號“;”。
  • 也允許屬性,因為在內部它們也是方法
public class MyClass : ICanDoThis {
    public void TheThingICanDo(){
        // do the thing
    }

    public int SomeValueProperty { get; set; }
    public int SomeValueNotImplemtingAnything { get; set; }
}

ICanDoThis obj = new MyClass();

// ok
obj.TheThingICanDo();

// ok
obj.SomeValueProperty = 5;

// Error, this member doesn't exist in the interface
obj.SomeValueNotImplemtingAnything = 5;

// in order to access the property in the class you must "down cast" it
((MyClass)obj).SomeValueNotImplemtingAnything = 5; // ok

當你使用 WinForms 或 WPF 等 UI 框架時,這尤其有用,因為必須從基類繼承以建立使用者控制元件,並且你無法通過不同的控制元件型別建立抽象。一個例子?接下來:

public class MyTextBlock : TextBlock {
    public void SetText(string str){
        this.Text = str;
    }
}

public class MyButton : Button {
    public void SetText(string str){
        this.Content = str;
    }
}

提出的問題是兩者都包含一些文字的概念,但屬性名稱不同。並且你無法建立建立*抽象基類,*因為它們具有對 2 個不同類的強制繼承。介面可以緩解這種情況

public interface ITextControl{
    void SetText(string str);
}

public class MyTextBlock : TextBlock, ITextControl {
    public void SetText(string str){
        this.Text = str;
    }
}

public class MyButton : Button, ITextControl {
    public void SetText(string str){
        this.Content = str;
    }

    public int Clicks { get; set; }
}

現在 MyButton 和 MyTextBlock 可以互換。

var controls = new List<ITextControls>{
    new MyTextBlock(),
    new MyButton()
};

foreach(var ctrl in controls){
    ctrl.SetText("This text will be applied to both controls despite them being different");

    // Compiler Error, no such member in interface
    ctrl.Clicks = 0;

    // Runtime Error because 1 class is in fact not a button which makes this cast invalid
    ((MyButton)ctrl).Clicks = 0;

    /* the solution is to check the type first.
    This is usually considered bad practice since
    it's a symptom of poor abstraction */
    var button = ctrl as MyButton;
    if(button != null)
        button.Clicks = 0; // no errors

   
}