Interface到底繼承於Object嗎?之我見

iDotNetSpace發表於2009-07-15
  今天和同事討論了一個問題:Interface到底繼承於Object嗎?
      我看過的所有關於.Net的書都告訴我“所有的型別都是繼承於System.Object的”,那麼理所當然
Interface也是繼承於System.Object的,以下面程式碼為例:

 

    class Program
    {
        
static void Main(string[] args)
        {
            IATM account 
= new ATMMachine();
            account.GetMoney(
600);
            Console.WriteLine(account.ToString());

            Console.ReadKey();
        }
    }

    
public interface IATM
    {
        
void GetMoney(Decimal amount);
    }

    
public class ATMMachine : IATM
    {
        
private Decimal b;

        
public void GetMoney(Decimal amount)
        {
            b 
+= amount;
        }

        
public override string ToString()
        {
            
return String.Format("Getted Money = {0,6:C}", b);
        }
    }

 

      將程式碼編譯以後用IL DASM反編譯後檢視IATMde IL程式碼,如下:
      .class interface public abstract auto ansi ConsoleApplication2.IATM
      {
      } // end of class ConsoleApplication2.IATM 
和其他型別做比較如:
      .class private auto ansi beforefieldinit ConsoleApplication2.Program
             extends [mscorlib]System.Object
      {
      } // end of class ConsoleApplication2.Program

      都有.Class這個標識,說明事實上Interface也是一個Class,只是它是一個特殊的Class,同時說明一個問題,
IATM介面並沒有繼承System.Object,否則就應該有extends [mscorlib]System.Object這句話。那麼到底Interface
繼承了什麼呢?利用AL DASM 檢視後設資料檔案(Ctrl+M)後有如下片段:其中02000003標識Interface:IATM,02000004標識Class:ATMMachine 
TypeDef #2 (02000003)
-------------------------------------------------------
TypDefName: ConsoleApplication2.IATM  (02000003)
Flags     : [Public] [AutoLayout] [Interface] [Abstract] [AnsiClass]  (000000a1)
Extends   : 01000000 [TypeRef] 
Method #1 (06000003) 
-------------------------------------------------------
MethodName: GetMoney (06000003)
Flags     : [Public] [Virtual] [HideBySig] [NewSlot] [Abstract]  (000005c6)
RVA       : 0x00000000
ImplFlags : [IL] [Managed]  (00000000)
CallCnvntn: [DEFAULT]
hasThis 
ReturnType: Void
1 Arguments
Argument #1:  ValueClass System.Decimal
1 Parameters
(1) ParamToken : (08000002) Name : amount flags: [none] (00000000)


TypeDef #3 (02000004)
-------------------------------------------------------
TypDefName: ConsoleApplication2.ATMMachine  (02000004)
Flags     : [Public] [AutoLayout] [Class] [AnsiClass] [BeforeFieldInit]  (00100001)
Extends   : 01000001 [TypeRef] System.Object
Field #1 (04000001)
-------------------------------------------------------
Field Name: b (04000001)
Flags     : [Private]  (00000001)
CallCnvntn: [FIELD]
Field type:  ValueClass System.Decimal

Method #1 (06000004) 
-------------------------------------------------------
MethodName: GetMoney (06000004)
Flags     : [Public] [Final] [Virtual] [HideBySig] [NewSlot]  (000001e6)
RVA       : 0x0000208f
ImplFlags : [IL] [Managed]  (00000000)
CallCnvntn: [DEFAULT]
hasThis 
ReturnType: Void
1 Arguments
Argument #1:  ValueClass System.Decimal
1 Parameters
(1) ParamToken : (08000003) Name : amount flags: [none] (00000000)
      注意Extends標識,它表明當前型別繼承了什麼型別,從這裡也可以看出IATM沒有繼承System.Object,否則應該有Extends   : 01000001 [TypeRef] System.Object這句描述,
      01000000 [TypeRef] 是什麼呢?我理解是什麼都不繼承,開啟mscorlib.dll有如下後設資料描述片段:
TypeDef #1 (02000002)
-------------------------------------------------------
TypDefName: System.Object  (02000002)
Flags     : [Public] [AutoLayout] [Class] [Serializable] [AnsiClass] [BeforeFieldInit]  (00102001)
Extends   : 01000000 [TypeRef] 
Method #1 (06000001) 
-------------------------------------------------------
MethodName: .ctor (06000001)
Flags     : [Public] [HideBySig] [ReuseSlot] [SpecialName] [RTSpecialName] [.ctor]  (00001886)
RVA       : 0x000020d0
ImplFlags : [IL] [Managed]  (00000000)
CallCnvntn: [DEFAULT]
hasThis 
ReturnType: Void
No arguments.
CustomAttribute #1 (0c000001)
-------------------------------------------------------
CustomAttribute Type: 06003055
CustomAttributeName: System.Runtime.ConstrainedExecution.ReliabilityContractAttribute :: instance void .ctor(value class System.Runtime.ConstrainedExecution.Consistency,value class System.Runtime.ConstrainedExecution.Cer)
Length: 12
Value : 01 00 03 00 00 00 01 00  00 00 00 00             >                <
ctor args: ( )

這裡的Extends標識後的內容也是01000000 [TypeRef],所以我推測Interface和System.Object都沒有繼承於任何型別。
      實際上,這裡的01000000(即0X01000000)是後設資料標識(Metadata Token),它是個4-byte的值,最高位用來標識當前的後設資料型別,例如:01代表該條後設資料是一個TypeRef,02代表TypeDef,04代表FieldDef,06代表MethodDef,08代表 ParamDef,對於最低3位,簡單的說可以看成當前後設資料在後設資料表中的索引(實際上後設資料表的儲存是很複雜的),例如0x0400001B代表這條後設資料是一個Field,它在後設資料表中的索引位置是第27行,那麼從0X01000000可以看出,它表示的是當前後設資料標識的型別是一個TypeRef並且它在後設資料表中的索引位置是0,其實在後設資料表的索引位置0處是不儲存任何資料的,所以這樣的標識被稱作“Nil”標識。

      最後是一個疑問:
            IATM account = new ATMMachine();      IATM account;           (當然這樣寫會編譯不通過,但是智慧感知依然有效)
            account.GetMoney(600);                 或者           account.GetMoney(600);  
                             
      在IDE中敲入account.後會發現除了IATM中定義的GetMoney方法外還有ToString、GetType、Equals、GetHashCode這四個方法,那麼現在明明是用IATM介面來訪問物件,而IATM並沒有Extends System.Object,所以這裡智慧感知應該只出現GetMoney這個方法才對,可是事實卻不是這樣,那麼這是為什麼呢?我也沒有從後設資料中找到答案,希望各位高手能指點一二。

      另外我做如下猜測:通過介面所能訪問的應該只有Class或者Struct,而只要是託管程式碼中的型別,不管是Class還是Struct都是繼承於System.Object的(Struct繼承於System.ValueType,而System.ValueType繼承於System.Object),因此System.Object中定義的以上四個方法總是對外公開的,所以我感覺是不是在邏輯上可以看成是Interface和System.Object都Extends了一個擁有這四種方法的“介面”,所有的Interface都隱式提供了上述四種契約而在.NET編譯器編譯時令所有介面包含這四種契約。 最後,通過修改後設資料可以去掉Extends   : 01000000 ....然後編譯成新的dll並且這個dll可用,我覺得這個時候的程式碼已經不是託管程式碼了,不符合CLS規範了,所以以此來推出並不是所有型別都繼承於System.Object可能不太合適。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12639172/viewspace-609228/,如需轉載,請註明出處,否則將追究法律責任。

相關文章