界面基础知识

接口的功能称为功能契约。这意味着它声明了属性和方法,但它没有实现它们。

所以不像类接口:

  • 无法实例化
  • 不能有任何功能
  • 只能包含方法* (属性和事件是内部方法)
  • 继承接口称为实现
  • 你可以从 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

   
}