[VS2010] 型別等價(Type Equivalence)

[VS2010] 型別等價(Type Equivalence)

型別等價 (Type Equivalence) 是 .NET Framework 4.0 引入的新功能,它的作用是將型別的符號匯入到目標的組件中,讓來源的組件就算是版本變更了,其用戶端程式也可以在不用編譯的情況下仍可正常執行,這個功能是做給 COM 元件用的,它可以讓用戶端程式直接匯入 COM 型別函式庫,並且不用再多部署 COM 型別函式庫即可直接呼叫 COM DLL 或元件,不過若是 Managed code,仍然可以使用這個功能。不過,由於這個功能是給 COM 元件使用的 (.NET 元件不入用 [ComImport] 來開放介面),所以在 Managed Code 上用效益並不明顯,若沒有用到 COM 元件的話,這個功能其實不用在意太多。

 

若要做到 Managed Code 的型別等價,元件端必須要具備下列條件:

1. 至少開放一個介面。

2. 在介面中使用 [ComImport] 來宣告,並且設定介面的 Guid 值。

3. 在組件的資訊檔中,使用 [assembly: ImportFromTypeLib(“”)] 設定,並且也要設定組件層級的 GUID 值 (這是目前 Visual Basic 和 C# 預設就會有的功能)。

在滿足上述條件以後,於用戶端專案在要使用型別等價的元件上,設定 Embed Interop Type 為 true (在屬性視窗),如此可以將型別等價的介面內嵌到用戶端應用程式中,如此就算是實作它的元件 (runtime component) 發生變化,用戶端程式也無需再次編譯即可正常執行,也就是說,就算 runtime component 不同 (例如 Office 2003/2007),只要型別等價的介面是相同的話,用戶端仍可以正常執行。不過實作型別等價的程序有一點複雜,以下簡單說明如何建構簡單的型別等價程式:

 

A. 建立一個要作為型別等價介面的元件類別庫專案,命名為 TypeEquivalenceInterface,然後設定其專案輸出到一個指定的位置 (可以是方案目錄內的一個新子目錄,例如 \Binaries):

image

 

B. 接著,將原本的 Class1.cs,更名為 ISampleInterface.cs,其內部宣告也會自動變為 class ISampleInterface,請將 class 改為 interface,並加入 [ComImport] 以及 [Guid] 屬性,例如下列程式所示:

 

using System.Runtime.InteropServices;

namespace TypeEquivalenceInterface
{
    [ComImport]
    [Guid("B4D28905-DCA2-4B1E-864A-A96AC967D084")]

    public interface ISampleInterface
    {
        string VersionData { get; }
        int GetDataV1(); 
    }
}

 

在上面的範例中,Guid 請變更為一組新的,讀者可以利用 Visual Studio 的 Tools/Generate GUID 工具來產生新的 GUID,然後將原本的替換掉。

 

C. 在專案中打開 Properties/AssemblyInfo.cs,並加入下列的指令,然後建置它:

 

[assembly: ImportFromTypeLib(“”)]

 

D. 新增一個要實作型別等價介面的類別庫,命名為 TypeEquivalenceImpl,與前一個元件一樣,設定輸出路徑到相同的位置後,加入 TypeEquivalenceInterface 元件的參考 (請注意:要由輸出目錄去加,不能使用加入專案方式)。然後在加入的 TypeEquivalenceInterface 元件的參考屬性中,設定 Specify version 為 FALSE:

 

image

 

E. 更名 Class1.cs 為 SampleClass.cs,然後以下列程式碼作為實作碼,並建置它。

 

namespace TypeEquivalenceImpl
{
    public class SampleClass : TypeEquivalenceInterface.ISampleInterface
    {
        public string VersionData { get { return "version 1 stamp."; } }
        public int GetDataV1()
        {
            return 121;
        }

    }
}

 

F. 新增用戶端程式 (主控台應用程式),命名為 TypeEquivalenceClient,並如同前一個專案般設定相同的輸出路徑,然後在輸出路徑中加入 TypeEquivalenceInterface.dll 的參考 (不必加 TypeEquivalenceImpl.dll),並且將 TypeEquivalenceInterface 的參考屬性中的 Embed Interop Type 為 TRUE:

 

image

 

G. 使用下列程式碼作為用戶端程式,並按 CTRL+F5,可以看到這樣的畫面:

 

namespace TypeEquivalenceClient
{
    class Program
    {
        static void Main(string[] args)
        {
            Assembly runtimeLibrary = Assembly.Load("TypeEquivalenceImpl");

            ISampleInterface sampleClass =
                (ISampleInterface)runtimeLibrary.CreateInstance("TypeEquivalenceImpl.SampleClass");

            Console.WriteLine("version data: " + sampleClass.VersionData);
            Console.WriteLine("GetDataV1(): " + sampleClass.GetDataV1());
            sampleClass = null;

            Console.ReadLine();
        }
    }
}

 

image

 

至此,整個架構就完成了。

 

H. 請修改 TypeEquivalenceInterface,將版本號改為 2.0.0.0,並且在程式中加入一個成員:

 

using System.Runtime.InteropServices;

namespace TypeEquivalenceInterface
{
   
[ComImport]
    [Guid("B4D28905-DCA2-4B1E-864A-A96AC967D084")]
    public interface ISampleInterface
    {
        string VersionData { get; }
        int GetDataV1(); 
       
int GetDataV2();
    }
}

 

並且配合修改 TypeEquivalenceImpl 以增加實作,並且一樣將它的版本號改為 2.0.0.0:

 

namespace TypeEquivalenceImpl
{
    public class SampleClass : TypeEquivalenceInterface.ISampleInterface
    {
        public string VersionData { get { return "version 1 stamp."; } }
        public int GetDataV1()
        {
            return 121;
        }

        public int GetDataV2()
        {
            return 250;
        }

    }
}

 

然後編譯 TypeEquivalenceInterface 以及 TypeEquivalenceImpl,但是不要建置 TypeEquivalenceClient,直接到輸出目錄執行 TypeEquivalenceClient,你會發現程式仍可正常執行不受影響。

 

參考資料:

Walkthrough: Embedding Types from Managed Assemblies (C# and Visual Basic)

http://msdn.microsoft.com/en-us/library/dd409610(VS.100).aspx