C# 4.0中COM互操作性和方差得到增強
原創(chuàng)【51CTO獨家特稿】這里將為大家介紹C# 4.0當中的COM互操作性以及方差這兩個新特性。51CTO之前曾報道過C#4.0新特性一覽及歷史回顧。
增強的COM互操作性特性
C# 4.0在COM互操作性支持方面提供了很多改進,使得你現(xiàn)在在.NET代碼中可以更容易地調(diào)用COM對象了,需要執(zhí)行的類型轉(zhuǎn)換更少了,ref關(guān)鍵字不再需要,也不再需要主互操作程序集或PIA,從本質(zhì)上講,C# 4.0提供了大量的新特性,特別是在COM互操作方面更是下足了功夫,在前一篇文章中談到的動態(tài)查詢和命名/可選參數(shù)的支持,幫助提高了與COM API如Office自動化API互操作的體驗。
例如,下面是C# 4.0以前的版本打開一個Word文件的代碼:
- using Word = Microsoft.Office.Interop.Word;
- namespace COMInterop
- {
- class Program
- {
- static void Main(string[] args)
- {
- Word.Application wordApplication =
- new Word.Application() { Visible = true };
- object missingValue = System.Reflection.Missing.Value;
- object readOnlyValue = true;
- object fileName = @"C:\\DevX.docx";
- wordApplication.Documents.Open(ref fileName, ref
- missingValue, ref readOnlyValue,
- ref missingValue, ref missingValue,
- ref missingValue, ref missingValue,
- ref missingValue, ref missingValue,
- ref missingValue, ref missingValue,
- ref missingValue, ref missingValue,
- ref missingValue,ref missingValue);
- }
- }
- }
在最后的open調(diào)用中,你可以看到需要傳遞大量的可選參數(shù)以滿足函數(shù)調(diào)用,隨著C# 4.0中可選和命名參數(shù)的引入,現(xiàn)在做同樣的事情,需要的代碼要少得多了。
下面是C# 4.0中用來打開Word文檔的代碼:
- using Word = Microsoft.Office.Interop.Word;
- namespace COMInterop
- {
- class Program
- {
- static void Main(string[] args)
- {
- Word.Application wordApplication = new
- Word.Application() {Visible = true};
- wordApplication.Documents.Open(@"C:\\DevX.docx",
- ReadOnly: true);
- }
- }
- }
性能改善
PIA是由COM接口產(chǎn)生的,你可以在你的應用程序代碼中使用這個程序集以一種強類型的方式與COM對象互操作,但它們很笨重,很消耗內(nèi)存,會顯著降低應用程序的性能。相反,互操作程序集通過前面的代碼編譯生成,只包含你的應用程序真正用到的互操作代碼,從而大大減少了程序集的大小,提高了應用程序的性能。
動態(tài)導入大多數(shù)COM函數(shù)都是接收并返回變量類型,在PIA中表示為對象,因此,當你使用這些方法時,需要使用適當?shù)膹娭祁愋娃D(zhuǎn)換,但在C# 4.0中,你可以使用dynamic關(guān)鍵字代替COM函數(shù)中的object,因此,現(xiàn)在不必再進行類型轉(zhuǎn)換了。
思考一下下面的代碼,你需要使用轉(zhuǎn)換在excel文檔中為某個特定單元格設(shè)置一個值:
- ((Excel.Range)excelObj.Cells[5, 5]).Value =
- "This is sample text";
上面的代碼需要使用強制類型轉(zhuǎn)換,但在C# 4.0中,你可以消除掉強制類型轉(zhuǎn)換了,這一切都得感謝dynamic關(guān)鍵字,下面是C# 4.0中的實現(xiàn)方法:
- excelObj.Cells[5, 5].Value = "This is sample text";
下面是一個更為完整的例子,使用的是C# 3.0代碼保存excel文檔:
- using Excel = Microsoft.Office.Interop.Excel;
- namespace COMInterop
- {
- class Program
- {
- static void Main(string[] args)
- {
- var excelApplication = new Excel.Application();
- excelApplication.Visible = true;
- dynamic excelWorkBook =
- excelApplication.Workbooks.Add(
- System.Reflection.Missing.Value);
- Excel.Worksheet wkSheetData = (
- Excel.Worksheet)excelWorkBook.ActiveSheet;
- excelWorkBook.SaveAs("Testfile.xls",
- System.Reflection.Missing.Value,
- System.Reflection.Missing.Value,
- System.Reflection.Missing.Value,
- System.Reflection.Missing.Value,
- System.Reflection.Missing.Value,
- Excel.XlSaveAsAccessMode.xlShared,
- System.Reflection.Missing.Value,
- System.Reflection.Missing.Value,
- System.Reflection.Missing.Value,
- System.Reflection.Missing.Value,
- System.Reflection.Missing.Value);
- }
- }
- }
在C# 4.0中,你不再需要使用缺失值和明確的強制類型轉(zhuǎn)換了,下面是在C# 4.0中沒有使用缺失值和強制類型轉(zhuǎn)換的代碼:
- using Excel = Microsoft.Office.Interop.Excel;
- namespace COMInterop
- {
- class Program
- {
- static void Main(string[] args)
- {
- var excelApplication = new Excel.Application();
- excelApplication.Visible = true;
- dynamic excelWorkBook = excelApplication.Workbooks.Add();
- Excel.Worksheet wkSheetData = excelWorkBook.ActiveSheet;
- excelWorkBook.SaveAs(
- "Testfile.xls",
- AccessMode: Excel.XlSaveAsAccessMode.xlShared);
- }
- }
- }
注意:COM有一個和托管代碼完全不同的編程模型,為了調(diào)用COM函數(shù),C#編譯器允許你傳遞參數(shù)值,并會產(chǎn)生臨時變量來保存這些值,當函數(shù)調(diào)用完畢后這些變量就會被丟掉。
方差支持
在C# 4.0中,你可以對泛型類型指定in(僅輸入)好out(只返回)參數(shù),這些參數(shù)可以作為唯一的輸入?yún)?shù)或只作為這種類型的返回值被傳遞。
C# 4.0中對方差有兩方面的支持:協(xié)方差和方差。如果你必須使用完全匹配正式類型的名稱,那么返回的值或參數(shù)是不變的。如果你能夠使用更多的衍生類型作為正式參數(shù)類型的代替物,那么參數(shù)是可變的。如果你能夠?qū)⒎祷氐念愋头峙浣o擁有較少類型的變量,那么返回的值是逆變的。
我這里不涉及任何不變量參數(shù),你也應該從C#早期版本中熟悉了。
協(xié)方差
使用一個簡單的例子更容易理解協(xié)方差,注意string是一個特殊的類型,而object是一個泛型類型,因此string是協(xié)變到object的。下面我們來看看C# 4.0中是如何支持協(xié)方差的,在C# 4.0中定義的IEnumerable
- public interface IEnumerable : IEnumerable
- {
- IEnumerator GetEnumerator();
- }
- public interface IEnumerator : IEnumerator
- {
- bool MoveNext();
- T Current { get; }
- }
在c#的早期版本中,IEnumerable<string>不是IEnumerable<object>。注意前面Ienumerator<out T>定義中的out參數(shù),它表明普通的T只可以在輸出位置,否則編譯器就會報告錯誤,T中的接口是協(xié)變的,意味著IEnumerable<P>也是 IEnumerable<Q>用P替換Q所得,因此,一個字符串序列也是一個對象序列,因此下面的語句完全有效:
IEnumerable<object> someObj = new List<string>();
下面的例子說明了你如何在C# 4.0中使用協(xié)方差:
- namespace Test
- {
- class Base
- {
- //Methods and Properties of the Base Class
- }
- class Derived : Base
- {
- //Methods and Properties of the Derived Class
- }
- class Program
- {
- delegate T TestFunction<out T>();
- static void Main(string[] args)
- {
- TestFunction<Derived> derivedObj = () => new Derived();
- TestFunction<Base> baseObj = derivedObj;
- }
- }
- }
逆變性
C# 4.0中泛型接口的類型參數(shù)可以在in修飾字中,允許它們只出現(xiàn)在輸入位置,例如:
- public interface IComparer<in T>
- {
- public int Compare(T left, T right);
- }
因此,比較器既可以比較對象也可以比較字符串,這就叫做逆變性。逆變性的一個例子就是Equals()函數(shù)和CompareTo()函數(shù)。如果你有一個函數(shù)可以比較兩個基類的實例,那么你也可以使用它比較兩個派生類的實例,你可以在一個類型對象的實例中存儲任何函數(shù)調(diào)用的結(jié)果,因為C#中函數(shù)返回的類型是逆變的。
下面是前面例子的逆變副本:
- namespace Test
- {
- class Base
- {
- }
- class Derived : Base
- {
- }
- class Program
- {
- delegate void TestDelegate<in T>(T a);
- static void Main(string[] args)
- {
- TestDelegate<Base> baseObj = (obj) => {
- System.Console.WriteLine(obj); };
- TestDelegate<Derived> derivedObj = baseObj;
- }
- }
- }
注意:類型方差只能在接口和委派類型上工作,只有在類型參數(shù)之間存在引用轉(zhuǎn)換才可以應用方差,因此,IEnumerable<int>不是IEnumerable<object>,因為int是一個值類型,object是一個引用類型,也就是說,整數(shù)到對象的轉(zhuǎn)換是箱子轉(zhuǎn)換,而不是一個引用轉(zhuǎn)換,因此它不是一個方差的例子。
正如你所看到的,C# 4.0的新特性和主互操作程序集一起可以工作得更好,對可選參數(shù)的支持意味著你不用明確地傳遞缺失值給函數(shù),而是用可選參數(shù)進行替代,對命名參數(shù)的支持意味著不用按照特定的順序傳遞值,只要你使用了名稱就可以識別,對方差的支持消除了許多冗長的,容易出錯的強制類型轉(zhuǎn)換,總而言之,這些都是十分有益的變化。
【編輯推薦】