天天看點

[C#1] 11-接口接口與繼承使用接口改變已裝箱值類型中的字段實作有多個相同方法的接口顯示接口成員實作

接口與繼承

CLR規定一個類型隻能有一個基類型,這種繼承成為單繼承;

接口繼承是指一個類型繼承的是接口中的方法簽名,而非方法實作,通常稱為實作接口; 接口僅僅是含有一組虛方法的抽象類型,不含有任何實作。CLR允許接口包含靜态方法、靜态字段、常量、以及靜态構造器, 但是CLS相容的接口類型是不允許有任何靜态成員的,因為一些程式設計語言不能定義或者通路它們。 C#語言就是如此,C#編譯器不允許接口中有任何靜态成員。

約定接口名稱第一個字母是大寫的I;接口可以多繼承,實際上實作了多個接口的類型允許我們将它的對象看作這個接口中的任意一個。 就好比是一個人有很多能力,他會遊泳[可以看多是運動員],他會程式設計[程式員]。 值類型也可以實作接口,當我們把一個值類型執行個體轉為接口類型時,會被裝箱,因為接口總被認為是引用類型,并且它們的方法總是虛方法。未裝箱的值類型是沒有方法表指針的,執行裝箱将使CLR可以查詢類型的方法表,便可以調用其虛方法了。

抽象類:is-a的關系;接口:can-do的關系。

使用接口改變已裝箱值類型中的字段

public struct Location
{
    public int x, y;
 
    public void Change(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
    public override string ToString()
    {
        return string.Format("[{0}-{1}]", x.ToString(), y.ToString());
    }
}
public class Test
{
    static void Main()
    {
        Location l = new Location();
        l.x = l.y = 6;
        Console.WriteLine(l);//[6-6]
        l.Change(5, 5);
        Console.WriteLine(l);//[5-5]
        object o = l;
        Console.WriteLine(o);//[5-5]
 
        //o對Change方法一無所知,是以先轉型為Location
        //然而這樣會執行拆箱操作,線上程堆棧上生成一個
        //臨時的Location,當改變它的字段時,原有的已裝
        //箱的<o>則不受這樣的影響
        ((Location)o).Change(9, 9);
        //[5-5]
        Console.WriteLine(o);
    }
}      

上述代碼我們無法改變以裝箱的值類型中字段,我們可以用接口來欺騙C#使其改變值類型的字段[Location : IChangeBoxedLocation]:

1 public interface IChangeBoxedLocation
 2 {
 3     void Change(int x, int y);
 4 }
 5 public class Test
 6 {
 7     static void Main()
 8     {
 9         Location l = new Location();
10         l.x = l.y = 6;
11         object o = l;
12         //轉型為接口須裝箱,改變已裝箱的對象,最後丢棄改變
13         ((IChangeBoxedLocation)l).Change(5, 5);
14         Console.WriteLine(l);//[6-6]
15  
16         //因為o已經是引用類型,接口方法Change允許我們修改o中的字段
17         ((IChangeBoxedLocation)o).Change(9, 9);
18         Console.WriteLine(o);//[9-9]
19     }
20 }      

我的通俗了解是:我是一個人[值類型Location],我有程式設計的能力[實作IChangeBoxedLocation接口], 是以在必要的時候你可以把我當作程式員來使用。

實作有多個相同方法的接口

1 public interface IWindow
 2 {
 3     void Print();
 4 }
 5  
 6 public interface IConsole
 7 {
 8     void Print();
 9 }
10  
11 public class MyClass : IWindow, IConsole
12 {
13     void IWindow.Print()
14     {
15         //....
16     }
17     void IConsole.Print()
18     {
19         //....
20     }
21     public void Print()
22     {
23         //....
24     }
25 }      

MyClass實作了多了Print方法,是以我們要告訴C#編譯器我們的哪一個Print實作了哪個接口,C#中通過在方法名前面加上接口名來告訴C#編譯器。public void Print()則是一個普通的方法而已,與接口沒有任何關系【如果我們把void IWindow.Print()删除,則這個方法[public void Print()]将是實作IWindow接口的方法,C#編譯器在辨析接口成員實作是,會按照先完全限定接口成員後非完全限定成員的順序進行辨析】。

上面的兩個完全限定接口方法沒有聲明為public,這是因為這些方法會有雙重身份,有時共有[類型轉型為該接口類型時:MyClass轉為 IWindow或者 IConsole時],有時私有[MyClass執行個體時]。在一個類型中用完全限定名定義接口方法時,該方法被認為是私有的,因為類型本身無法調用它,當轉型為一個接口時,這個方法将可以被調用,這時又是一個共有方法

顯示接口成員實作

顯示實作接口成員正是用到了上面的用完全限定名來實作接口。顯示實作接口成員為應用程式提供了更多的類型安全。

作者:

Blackheart

出處:

http://linianhui.cnblogs.com

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

繼續閱讀