熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

C#4.0動態關鍵字與COM

2022-06-13   來源: .NET編程 

  在我成為 C/ 開發人員之後尤其是在 Microsoft 推出之前我經常指責采用 Visual Basic 進行編程的同事選擇使用那樣一種弱類型化的語言
   
    有那麼一段時間進行靜態類型化和強類型化編程是獲得良好的體驗的明顯選擇但是事物總是要發展變化的當今的 開發人員社區(看起來幾乎所有前 C/C++ 開發人員都已經轉移到這裡)經常發現他們明確需要一個更加動態的編程模型上個月我介紹了 Microsoft 在 C# 和 Visual Studio 中提供的一些動態編程功能這個月我將深入探討一些相關方案首先要介紹 C# 最吸引人的原因之一可以在 NET Framework 中輕松實現 COM 對象編程
   
    輕松訪問 COM 對象
   
    如果一個對象的結構和行為不是由完全靜態定義的類型(編譯器全面了解該類型)描述的話該對象就是動態的不可否認動態一詞在這種情況下聽起來太寬泛了因此讓我們看一個簡單的示例在 Script 等腳本語言中以下代碼能夠成功運行
   
    Set word = CreateObject(WordApplication
   
    CreateObject 函數假設它獲得的 string 參數是某個已注冊 COM 對象的 progID它創建該組件的一個實例並返回該實例的 IDispatch 自動化接口IDispatch 接口的細節在腳本語言的任何層級都絕對看不到重要的是您可以編寫如下代碼
   
    Set word = CreateObject(WordApplication
   
    wordVisible = True
   
    Set doc = wordDocumentsAdd()
   
    Set selection = wordSelection
   
    selectionTypeText Hello world
   
    selectionTypeParagraph()
   
    docSaveAs(fileName)
   
   


   
    在這段代碼中您首先創建對組件的引用以便自動執行底層 Microsoft Word 應用程序的行為接著您顯示 Word 主窗口添加一個新文檔在其中輸入一些文字然後將文檔保存到某個位置這段代碼清晰易懂而且更重要的是能夠正常運行
   
    但它能正常運行要歸功於 Script 提供的特殊功能後期綁定後期綁定意味著直到執行流程遇到給定對象之前該對象的類型都是未知的當執行流程需要執行給定對象時運行時環境才會開始確保要調用的該對象成員確實存在然後再進行調用在代碼真正執行之前不會對其進行任何提前檢查
   
    您可能知道像 這樣的腳本語言並沒有編譯器但是Visual Basic(包括 CLR 版本)多年來一直有一項類似的功能我承認我經常會羨慕我的 Visual Basic 同事能夠更輕松地使用 COM 對象而需要進行互操作的應用程序(例如 Office)經常采用這種有價值的構造塊事實上在有些情況下即使整個應用程序是用 編寫的我的團隊也會用 Visual Basic 編寫一部分互操作代碼這有點令人意外?多語言編程不是一種新的前沿技術嗎?
   
    在 Visual Basic 中CreateObject 函數的存在是為了解決頑固的兼容性問題重點在於基於 的語言在設計時考慮的是前期綁定NET Framework 能夠處理 COM 互操作性方案但是這種方案從來不能由編程語言通過關鍵字和工具來提供支持這種情況直到 C# 才有所改觀
   
    C# (和 Visual Basic)擁有動態查詢功能這表明後期綁定現在對於 NET Framework 開發人員來說已經切實可行了借助動態查詢功能您可以繞過靜態類型檢查在代碼中直接訪問方法屬性索引生成器屬性和字段而留待運行時進行解析
   
   


   
    還通過識別成員聲明中的默認值來實現可選參數這意味著在調用擁有可選參數的成員時可以省略可選參數而且既可以按名稱也可以按位置來傳遞參數最後C# 中改進的 COM 綁定功能意味著以前是靜態且強類型化的語言現在也支持腳本語言的一些常見功能在您了解如何利用新的動態關鍵字實現與 COM 對象的流暢操作之前讓我們稍稍深入了解一下動態類型查詢的內部機制
   
    動態語言運行時
   
    當您在 Visual Studio 中將某個變量聲明為動態時其默認配置中根本不會有 IntelliSense有趣的是如果您安裝一個類似 ReSharper (/resharper) 的附加工具就可以通過 IntelliSense 獲得一些有關動態對象的不完全信息 顯示了帶有和不帶 ReSharper 的代碼編輯器該工具僅僅列出該動態類型上看起來已經定義的成員在最低限度下動態對象是 SystemObject 的實例
   

  

  圖 在帶有和不帶 ReSharper 的情況下Visual Studio 中的動態對象的 IntelliSense
   
    讓我們看看當編譯器遇到以下代碼時會發生什麼情況(這段代碼設計得極其簡單目的是簡化對實現細節的理解)
   
    class Program
   
    {
   
    static void Main(string[] args)
   
    {
   
    dynamic x = ;
   
    ConsoleWriteLine(x)
   
    }
   
    }
   
    在第二行中編譯器不會嘗試解析符號 WriteLine也不會像傳統的靜態類型檢查器一樣發出警報或錯誤只要遇到 dynamic 關鍵字C# 的表現就會變得像是解釋性語言結果編譯器會生成一些臨時代碼用來解釋涉及動態變量或參數的表達式解釋器基於動態語言運行時 (DLR)是 機制中的一個全新組件若要使用更具體的術語編譯器必須使用 DLR 所支持的抽象語法來生成表達式樹並將其傳遞給 DLR 庫進行處理在 DLR 中由編譯器提供的表達式被封裝在動態更新的站點對象中站點對象負責實時將方法綁定到對象 顯示了真實代碼的充分簡化版本該真實代碼是由前述的簡單程序生成的
   
   


   
    圖 中的代碼已經過編輯和簡化以方便閱讀但它顯示了實際情況的要點動態變量映射到 SystemObject 實例然後就會在 DLR 中為程序創建一個站點該站點負責管理 WriteLine 方法及其參數與目標對象之間的綁定該綁定維持在類型 Program 的上下文中為了對動態變量調用方法 ConsoleWriteLine您將調用該站點並傳遞目標對象(本例中為 Console 類型)及其參數(本例中為動態變量)該站點將在內部檢查目標對象是否真的擁有成員 WriteLine並且該成員能夠接受類似於變量 x 中目前存儲的對象這樣的參數如果有任何問題 運行時就會引發 RuntimeBinderException
   
    圖 動態變量的真正實現
   
    internal class Program
   
    {
   
    private static void Main(string[] args)
   
    {
   
    object x = ;
   
    if (MainSiteContainersite == null)
   
    {
   
    MainSiteContainersite = CallSite<
   
    Action<CallSite Type object》
   
    Create(BinderInvokeMember(
   
    WriteLine
   
    null
   
    typeof(Program)
   
    new CSharpArgumentInfo[] {
   
    CSharpArgumentInfoCreate(…)
   
    }))
   
    }
   
    MainSiteContainersiteTargetInvoke(
   
    site typeof(Console) x)
   
    }
   
    private static class MainSiteContainer
   
    {
   
    public static CallSite<Action<CallSite Type object》 site;
   
    }
   
    }
   
   


   
    使用 COM 對象
   
    現在新的 能夠在基於 的應用程序中簡單輕松地使用 COM 對象讓我們看看如何在 C# 中創建一個 Word 文檔並且對您在 NET NET 中需要的代碼進行比較示例應用程序將基於給定的模板創建一個新的 Word 文檔填入一些內容並將其保存到一個指定位置模板包含一些書簽用於容納一些常用信息無論您面向的是 NET Framework 還是 NET Framework 通過編程來創建 Word 文檔的第一步都是添加 Microsoft Word 對象庫(請參見圖
   
    圖 引用 Word 對象庫
   
    在 Visual Studio NET Framework 之前若要完成此操作您需要類似圖 所示的代碼
   
    圖 在 C# 中創建新的 Word 文檔
   
    public static class WordDocument
   
    {
   
    public const String TemplateName = @Sampledotx;
   
    public const String CurrentDateBookmark = CurrentDate;
   
    public const String SignatureBookmark = Signature;
   
    public static void Create(String file DateTime now String author)
   
    {
   
    // Must be an Object because it is passed as a ref
   
    Object missingValue = MissingValue;
   
    // Run Word and make it visible for demo purposes
   
    var wordApp = new Application { Visible = true };
   
    // Create a new document
   
    Object template = TemplateName;
   
    var doc = wordAppDocumentsAdd(ref template
   
    ref missingValue ref missingValue ref missingValue)
   
    docActivate()
   
    // Fill up placeholders in the document
   
    Object bookmark_CurrentDate = CurrentDateBookmark;
   
    Object bookmark_Signature = SignatureBookmark;
   
    docBookmarksget_Item(ref bookmark_CurrentDate)RangeSelect()
   
    wordAppSelectionTypeText(currentToString())
   
    docBookmarksget_Item(ref bookmark_Signature)RangeSelect()
   
    wordAppSelectionTypeText(author)
   
    // Save the document
   
    Object documentName = file;
   
    docSaveAs(ref documentName
   
    ref missingValue ref missingValue ref missingValue
   
    ref missingValue ref missingValue ref missingValue
   
    ref missingValue ref missingValue ref missingValue
   
    ref missingValue ref missingValue ref missingValue
   
    ref missingValue ref missingValue ref missingValue)
   
    docClose(ref missingValue
   
    ref missingValue ref missingValue)
   
    wordAppQuit(ref missingValue
   
    ref missingValue ref missingValue)
   
    }
   
    }
   
   


   
    為了與 COM 自動化接口交互您經常需要 Variant 類型當您在基於 的應用程序中與 COM 自動化對象交互時您需要將 Variants 表示成普通對象其直接後果是您不能使用字符串來指示 Word 文檔所用的模板文件的名稱因為必須通過引用來傳遞 Variant 參數您不得不求助於 Object如下所示
   
    Object template = TemplateName;
   
    var doc = wordAppDocumentsAdd(ref template
   
    ref missingValue ref missingValue ref missingValue)
   
    要考慮的第二個方面是 Visual Basic 和腳本語言遠不如 嚴格例如這些語言不會強制要求您指定 COM 對象聲明上某個方法的所有參數Documents 集合的 Add 方法需要四個參數而除非您的語言支持可選參數否則就不能忽略這些參數
   
    正如前文所述C# 支持可選參數這意味著盡管直接用 C# 來重新編譯圖 中的代碼就能正常使用您可能仍會重寫這段代碼刪除所有用來傳遞缺少的值的 ref 參數如下所示
   
    Object template = TemplateName;
   
    var doc = wordAppDocumentsAdd(template)
   
    借助 C# 中新的省略 ref支持 中的代碼變得更加簡單而且更重要的是它變得更容易閱讀且語法上與腳本代碼更像 包含編輯過的版本該版本能夠用 C# 進行正確編譯並且與圖 中的代碼效果相同
   
    圖 在 C# 中創建新的 Word 文檔
   
    public static class WordDocument
   
    {
   
    public const String TemplateName = @Sampledotx;
   
    public const String CurrentDateBookmark = CurrentDate;
   
    public const String SignatureBookmark = Signature;
   
    public static void Create(string file DateTime now String author)
   
    {
   
    // Run Word and make it visible for demo purposes
   
    dynamic wordApp = new Application { Visible = true };
   
    // Create a new document
   
    var doc = wordAppDocumentsAdd(TemplateName)
   
    templatedDocumentActivate()
   
    // Fill the bookmarks in the document
   
    docBookmarks[CurrentDateBookmark]RangeSelect()
   
    wordAppSelectionTypeText(currentToString())
   
    docBookmarks[SignatureBookmark]RangeSelect()
   
    wordAppSelectionTypeText(author)
   
    // Save the document
   
    docSaveAs(fileName)
   
    // Clean up
   
    templatedDocumentClose()
   
    wordAppQuit()
   
    }
   
    }
   
   


   
    圖 中的代碼允許您使用普通 類型來調用 COM 對象而且可選參數使得它更加簡單
   
    中引入的動態關鍵字和其他 COM 互操作功能不會使代碼的運行速度明顯加快但能讓您像編寫腳本一樣編寫 C# 代碼對於 COM 對象來說這種成果可能與性能的提升一樣重要
   
    無 PIA 部署
   
    從 NET Framework 推出以來您就可以將 COM 對象包裝到托管類中然後從基於 NET 的應用程序中使用為了實現此目的您需要使用由 COM 對象的供應商提供的主互操作程序集 (PIA)PIA 必不可少必須與客戶端應用程序一起部署但是很多時候PIA 都太大了並且會包含整個 COM API因此將它們打包到安裝程序中不是什麼令人愉快的經驗
   
    Visual Studio 提供了無 PIA 選項無 PIA是指編譯器能夠嵌入您在當前程序集中從 PIA 獲取的必要定義因此只有真正需要的定義才會進入最終的程序集而不需要將供應商的 PIA 整個打包到安裝程序中 顯示了屬性框中的選項該選項在 Visual Studio 中實現了無 PIA
     
  

  圖 在 Visual Studio 中啟用無 PIA 選項
   
    無 PIA 功能基於 C# 的一項稱為類型等效性的功能簡而言之類型等效性就是兩個截然不同的類型可在運行時被當作是等效的並且可以互換使用類型等效性的典型示例是不同程序集中定義的兩個同名的接口它們是不同的類型但只要存在相同的方法它們就可以互換使用
   
    總之使用 COM 對象仍然代價不低但是 C# 中的 COM 互操作支持使您編寫的代碼簡單得多從基於 NET Framework 框架的應用程序處理 COM 對象可使您與傳統的應用程序和關鍵業務方案建立聯系如果不這樣做您的控制力就會大大降低COM 在 NET Frameworok 中是相當棘手的問題但動態功能使這個問題變得不那麼困難


From:http://tw.wingwit.com/Article/program/net/201311/11486.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.