1. 程式人生 > >C# 4.0新特性(白皮書)[轉]

C# 4.0新特性(白皮書)[轉]

Contents
目錄

  • Introduction 簡介
    • C# 4.0
  • Dynamic Lookup 動態查詢
    • The dynamic type dynamic型別
    • Dynamic operations 動態操作
    • Runtime lookup 執行時查詢
    • Example 示例
    • Overload resolution with dynamic arguments 帶有動態引數的過載解析
    • The Dynamic Language Runtime 動態語言執行時
    • Open issues 已知問題
  • Named and Optional Arguments 命名引數和可選引數
    • Optional parameters 可選引數
    • Named and optional arguments 命名的和可選的實參
    • Overload resolution 過載解析
  • Features for COM interop COM互操作特性
    • Dynamic import 動態引入
    • Compiling without PIAs 無PIA的編譯
    • Omitting ref 省略ref
    • Open issues 已知問題
  • Variance 變性
    • Covariance 協變性
    • Contravariance 逆變性
    • Limitations 限制
  • COM Example COM示例
  • Relationship with Visual Basic 與Visual Basic的關係
  • Resources 資源

Introduction
簡介

It is now close to a year since Microsoft Visual C# 3.0 shipped as part of Visual Studio 2008. In the VS Managed Languages team we are hard at work on creating the next version of the language (with the unsurprising working title of C# 4.0), and this document is a first public description of the planned language features as we currently see them.

Microsoft Visual C# 3.0作為Visual Studio 2008的一部分發布以來已經快一年了。在VS Managed Languages團隊,我們一直努力建立該語言的下一個版本(沒什麼驚喜,就是C# 4.0),而這個文件是我們現在所看到的、計劃中的語言特性的第一個公開描述。

Please be advised that all this is in early stages of production and is subject to change. Part of the reason for sharing our plans in public so early is precisely to get the kind of feedback that will cause us to improve the final product before it rolls out.

請記住本文內容都是針對產品早期的,而且將來會改變。這麼早地共享我們的計劃,一部分原因是為了獲取某些反饋,這些反饋會讓我們在產品最終釋出出來之前改進它。

Simultaneously with the publication of this whitepaper, a first public CTP (community technology preview) of Visual Studio 2010 is going out as a Virtual PC image for everyone to try. Please use it to play and experiment with the features, and let us know of any thoughts you have. We ask for your understanding and patience working with very early bits, where especially new or newly implemented features do not have the quality or stability of a final product. The aim of the CTP is not to give you a productive work environment but to give you the best possible impression of what we are working on for the next release.

與該白皮書一同釋出的還有Visual Studio 2010的第一個公開的CTP(社群技術預覽),它通過Virtual PC映像釋出,任何人都可以試用。請使用這個CTP來把玩和試驗這些特性,並讓我們知道您的想法。我們希望知道您對這個早期產品的理解和不滿,尤其是新添 加的實現特性是否不具備最終產品應有的質量和穩定性。這個CTP的目的不是為您提供一個高生產力的工作環境,而是讓您對我們正在為下一個釋出版做些什麼有 個印象。

The CTP contains a number of walkthroughs, some of which highlight the new language features of C# 4.0. Those are excellent for getting a hands-on guided tour through the details of some common scenarios for the features. You may consider this whitepaper a companion document to these walkthroughs, complementing them with a focus on the overall language features and how they work, as opposed to the specifics of the concrete scenarios.

該CTP包含了大量的演練,其中一些著重於C# 4.0的語言新特性。要詳細瞭解這些特性的常用場景,這些演練是絕好的上手教程。您可以認為該白皮書是這些演練的伴隨文件,全面地介紹了語言的特性和工作原理,而那些演練則是介紹具體的場景。

C# 4.0

The major theme for C# 4.0 is dynamic programming. Increasingly, objects are “dynamic” in the sense that their structure and behavior is not captured by a static type, or at least not one that the compiler knows about when compiling your program. Some examples include

C# 4.0的主要主題是動態程式設計。物件的意義變得越來越“動態”,它們的結構和行為無法通過靜態型別來捕獲,或者至少編譯器在編譯程式時無法得知物件的結構和行為。例如——

  • a. objects from dynamic programming languages, such as Python or Ruby
  • b. COM objects accessed through IDispatch
  • c. ordinary .NET types accessed through reflection
  • d. objects with changing structure, such as HTML DOM objects
  • a. 來自動態程式語言——如Python或Ruby——的物件
  • b. 通過IDispatch訪問的COM物件
  • c. 通過反射訪問的一般.NET型別
  • d. 結構發生過變化的物件——如HTML DOM物件

While C# remains a statically typed language, we aim to vastly improve the interaction with such objects.

儘管C#依然是靜態型別語言,但我們的目的是改善它與這些物件的互動。

A secondary theme is co-evolution with Visual Basic. Going forward we will aim to maintain the individual character of each language, but at the same time important new features should be introduced in both languages at the same time. They should be differentiated more by style and feel than by feature set.

另一個次要主題是與Visual Basic協同進步co-evolution )。將來我們希望僅維護每種語言單獨的特徵,而重要的新特性會同時引入兩種語言。它們的區別僅僅是風格和感覺上的,而不在於特性集方面。

The new features in C# 4.0 fall into four groups:

C# 4.0中的新特性分為四組——

Dynamic lookup
動態查詢

Dynamic lookup allows you to write method, operator and indexer calls, property and field accesses, and even object invocations which bypass the C# static type checking and instead gets resolved at runtime.

動態查詢允許在編寫方法、運算子和索引器呼叫、屬性和欄位訪問甚至物件呼叫時,繞過C#靜態型別檢查,而在執行時進行解析。

Named and optional parameters
命名引數和可選引數

Parameters in C# can now be specified as optional by providing a default value for them in a member declaration. When the member is invoked, optional arguments can be omitted. Furthermore, any argument can be passed by parameter name instead of position.

現在C#中的引數可以通過在成員宣告中為其提供預設值來指名它是可選的。在呼叫該成員時,可選引數可以忽略。另外,在傳入任何引數時都可以按照引數名而不是位置進行傳遞。

COM specific interop features
特定於COM的互操作特性

Dynamic lookup as well as named and optional parameters both help making programming against COM less painful than today. On top of that, however, we are adding a number of other small features that further improve the interop experience.

動態查詢以及命名引數和可選引數都有助於使針對COM的程式設計不再像今天這樣痛苦。在這些特性之上,我們還添加了大量其他小特性,進一步改善了互操作體驗。

Variance
變性

It used to be that an IEnumerable<string> wasn’t an IEnumerable<object> . Now it is – C# embraces type safe “co-and contravariance” and common BCL types are updated to take advantage of that.

過去,IEnumerable<string> 並不是IEnumerable<object> 。現在它是了——C#包含了型別安全的“協變性和逆變性(co-and contravariance)”而且通用的BCL也將利用這一特性進行更新。

Dynamic Lookup
動態查詢

Dynamic lookup allows you a unified approach to invoking things dynamically. With dynamic lookup, when you have an object in your hand you do not need to worry about whether it comes from COM, IronPython, the HTML DOM or reflection; you just apply operations to it and leave it to the runtime to figure out what exactly those operations mean for that particular object.

動態查詢可以用統一的方式來動態呼叫成員。有了動態查詢,當你拿到一個物件時,不用管它是來自於COM還是IronPython、HTML DOM或是反射;只需要對其進行操作即可,執行時會幫你指出針對特定的物件,這些操作的具體意義。

This affords you enormous flexibility, and can greatly simplify your code, but it does come with a significant drawback: Static typing is not maintained for these operations. A dynamic object is assumed at compile time to support any operation, and only at runtime will you get an error if it wasn’t so. Oftentimes this will be no loss, because the object wouldn’t have a static type anyway, in other cases it is a tradeoff between brevity and safety. In order to facilitate this tradeoff, it is a design goal of C# to allow you to opt in or opt out of dynamic behavior on every single call.

這給你帶來了巨大的靈活性,並能極大程度地精簡程式碼,但它伴隨著一個巨大的缺點——不會為這些操作維護靜態型別。在編譯 時,會假設動態物件支援任何操作,而如果它不支援某個操作,則只有到執行時才能得到錯誤。有的時候這不會有任何損失,因為物件根本不具有靜態型別,而且他 情況下必須在簡潔和安全之間進行權衡。為了幫助進行權衡,C#的一個設計目標就是允許在每個單獨的呼叫中選擇是否使用動態行為。

The dynamic type
dynamic 型別

C# 4.0 introduces a new static type called dynamic . When you have an object of type dynamic you can “do things to it” that are resolved only at runtime:

C# 4.0引入了一個新的靜態型別,稱為dynamic 。當你擁有了一個dynamic 型別的物件後,你“對他做的事情”只會在執行時進行解析——

view plain print ?
  1. dynamic d = GetDynamicObject(...);  
  2. d.M(7);  

The C# compiler allows you to call a method with any name and any arguments on d because it is of type dynamic . At runtime the actual object that d refers to will be examined to determine what it means to “call M with an int ” on it.

C#編譯器允許你使用任何引數在d 上呼叫一個方法,因為它的型別是dynamic 。執行時會檢查d 的實際型別,並檢測在它上面“用一個int 呼叫M ”是什麼意思。

The type dynamic can be thought of as a special version of the type object , which signals that the object can be used dynamically. It is easy to opt in or out of dynamic behavior: any object can be implicitly converted to dynamic , “suspending belief” until runtime. Conversely, there is an “assignment conversion” from dynamic to any other type, which allows implicit conversion in assignment-like constructs:

可以認為dynamic 型別是object 型別的一個特殊版本,指出了物件可以動態地使用。選擇是否使用動態行為很簡單——任何物件都可以隱式轉換為dynamic ,“掛起信任”直到執行時。反之,從dynamic 到任何其他型別都存在“賦值轉換”,可以類似於賦值的結構中進行隱式轉換——

view plain print ?
  1. dynamic d = 7;  // implicit conversion
  2. int  i = d;  // assignment conversion

Dynamic operations
動態操作

Not only method calls, but also field and property accesses, indexer and operator calls and even delegate invocations can be dispatched dynamically:

不僅是方法呼叫,欄位和屬性訪問、索引器和運算子呼叫甚至委託呼叫都可以動態地分派——

view plain print ?
  1. dynamic d = GetDynamicObject(…);  
  2. d.M(7); // calling methods
  3. d.f = d.P; // getting and settings fields and properties
  4. d[“one”] = d[“two”]; // getting and setting thorugh indexers
  5. int  i = d + 3;  // calling operators
  6. string  s = d(5,7);  // invoking as a delegate

The role of the C# compiler here is simply to package up the necessary information about “what is being done to d ”, so that the runtime can pick it up and determine what the exact meaning of it is given an actual object d . Think of it as deferring part of the compiler’s job to runtime.

C#編譯器在這裡的角色就是打包有關“在d 上做什麼”的必要資訊,使得執行時可以獲取這些資訊並檢測對於實際物件d 這些操作的確切含義。可以認為這是將編譯器的部分工作延遲到了執行時。

The result of any dynamic operation is itself of type dynamic .

任何動態操作的結果本身也是dynamic 型別的。

Runtime lookup
執行時查詢

At runtime a dynamic operation is dispatched according to the nature of its target object d :

在執行時,動態操作將根據目標物件d 的本質進行分派——

COM objects
COM物件

If d is a COM object, the operation is dispatched dynamically through COM IDispatch . This allows calling to COM types that don’t have a Primary Interop Assembly (PIA), and relying on COM features that don’t have a counterpart in C#, such as indexed properties and default properties.

如果d 是一個COM物件,則操作通過COM IDispatch 進行動態分派。這允許呼叫沒有主互操作程式集(Primary Interop Assembly,PIA)的COM型別,並依賴C#中沒有對應概念的COM特性,如索引屬性和預設屬性。

Dynamic objects
動態物件

If d implements the interface IDynamicObject d itself is asked to perform the operation. Thus by implementing IDynamicObject a type can completely redefine the meaning of dynamic operations. This is used intensively by dynamic languages such as IronPython and IronRuby to implement their own dynamic object models. It will also be used by APIs, e.g. by the HTML DOM to allow direct access to the object’s properties using property syntax.

如果d 實現了IDynamicObject 介面,則請求d 自身來執行該操作。因此通過實現IDynamicObject 介面,型別可以完全重新定義動態操作的意義。這在動態語言——如IronPython和IronRuby——中大量使用,用於實現他們的動態物件模型。API也會使用這類物件,例如HTML DOM允許直接使用屬性語法來訪問物件的屬性。

Plain objects
簡單物件

Otherwise d is a standard .NET object, and the operation will be dispatched using reflection on its type and a C# “runtime binder” which implements C#’s lookup and overload resolution semantics at runtime. This is essentially a part of the C# compiler running as a runtime component to “finish the work” on dynamic operations that was deferred by the static compiler.

除此之外,則d 是一個標準的.NET物件,操作是通過在其型別上進行反射來分派的,C#的“執行時繫結器(runtime binder)”實現了執行時的C#查詢和過載解析。其背後的本質是將C#編譯器作為執行時元件執行,來“完成”被靜態編譯器延遲的動態操作。

Example
示例

Assume the following code:

考慮下面的程式碼——

view plain print ?
  1. dynamic d1 =  new  Foo();  
  2. dynamic d2 = new  Bar();  
  3. string  s;  
  4. d1.M(s, d2, 3, null );  

Because the receiver of the call to M is dynamic , the C# compiler does not try to resolve the meaning of the call. Instead it stashes away information for the runtime about the call. This information (often referred to as the “payload”) is essentially equivalent to:

由於對M 進行呼叫的接受者是dynamic 型別的,C#編譯器不會試圖解析該呼叫的意義。而是將有關該呼叫的資訊儲存起來,供執行時使用。該資訊(通常稱作“有效載荷”)本質上等價於——

“Perform an instance method call of M with the following arguments:

  • 1. a string
  • 2. a dynamic
  • 3. a literal int 3
  • 4. a literal object null

“使用下面的引數執行一個稱作M 的例項方法——

  • 1. 一個string
  • 2. 一個dynamic
  • 3. 一個int 字面值3
  • 4. 一個object 字面值null

At runtime, assume that the actual type Foo of d1 is not a COM type and does not implement IDynamicObject . In this case the C# runtime binder picks up to finish the overload resolution job based on runtime type information, proceeding as follows:

在執行時,假設d1 的實際型別Foo 不是COM型別,也沒有實現IDynamicObject 。在這種情況下,C#執行時繫結器擔負起了過載解析的工作,這是基於執行時型別資訊完成的,按照下面的步驟進行處理——

  • 1. Reflection is used to obtain the actual runtime types of the two objects, d1 and d2 , that did not have a static type (or rather had the static type dynamic ). The result is Foo for d1 and Bar for d2 .
  • 2. Method lookup and overload resolution is performed on the type Foo with the call M(string,Bar,3,null) using ordinary C# semantics.
  • 3. If the method is found it is invoked; otherwise a runtime exception is thrown.
  • 1. 使用反射獲取兩個物件d1d2 的實際執行時型別,它們沒有靜態型別(包括靜態型別dynamic )。結果為d1Foo 型別而d2Bar
  • 2. 使用普通的C#語義在Foo 型別上對M(string,Bar,3,null) 呼叫進行方法查詢和過載解析。
  • 3. 如果找到了該方法,則呼叫它;否則丟擲執行時異常。

Overload resolution with dynamic arguments
帶有動態引數的過載解析

Even if the receiver of a method call is of a static type, overload resolution can still happen at runtime. This can happen if one or more of the arguments have the type dynamic :

即便方法呼叫的接受者是靜態型別的,過載解析依然發生在執行時。當一個或多個實參是dynamic 型別時就會出現這種情況——

view plain print ?
  1. Foo foo =  new  Foo();  
  2. dynamic d = new  Bar();  
  3. var result = foo.M(d);  

The C# runtime binder will choose between the statically known overloads of M on Foo , based on the runtime type of d , namely Bar . The result is again of type dynamic .

C#執行時繫結器會基於d 的執行時型別——也就是Bar ——在FooM 方法的靜態可知(statically known)過載之間進行選擇。其結果是dynamc 型別。

The Dynamic Language Runtime
動態語言執行時

An important component in the underlying implementation of dynamic lookup is the Dynamic Language Runtime (DLR), which is a new API in .NET 4.0.

動態語言執行時(Dynamic Language Runtime,DLR)是動態查詢的底層實現的一個重要元件,也是.NET 4.0中新增的API。

The DLR provides most of the infrastructure behind not only C# dynamic lookup but also the implementation of several dynamic programming languages on .NET, such as IronPython and IronRuby. Through this common infrastructure a high degree of interoperability is ensured, but just as importantly the DLR provides excellent caching mechanisms which serve to greatly enhance the efficiency of runtime dispatch.

DLR不僅為C#動態查詢,還為很多其他.NET上的動態語言——如IronPython和IronRuby——的實現提供了底層的基礎設施。這一通用基礎設施確保了高度的互操作性,更重要的是,DLR提供了卓越的快取機制,使得執行時分派的效率得到巨大的改善。

To the user of dynamic lookup in C#, the DLR is invisible except for the improved efficiency. However, if you want to implement your own dynamically dispatched objects, the IDynamicObject interface allows you to interoperate with the DLR and plug in your own behavior. This is a rather advanced task, which requires you to understand a good deal more about the inner workings of the DLR. For API writers, however, it can definitely be worth the trouble in order to vastly improve the usability of e.g. a library representing an inherently dynamic domain.

對於使用C#動態查詢的使用者來說,除了更高的效能之外,根本感覺不到DLR的存在。不過,如果你希望實現自己的動態分派物件,可以使用IDynamicObject 介面來與DLR互操作,並向其中插入自己的行為。這是一個非常高階的任務,要求對DLR的內部工作原理有相當深入的瞭解。對於編寫API的人,值得在這些問題上花些功夫,這樣能夠更廣泛地改善可用性,例如為一個本身就是動態的領域編寫類庫。

Open issues
已知問題

There are a few limitations and things that might work differently than you would expect.

這裡可能有一些限制或與你期望的結果不同。

  • The DLR allows objects to be created from objects that represent classes. However, the current implementation of C# doesn’t have syntax to support this.
  • Dynamic lookup will not be able to find extension methods. Whether extension methods apply or not depends on the static context of the call (i.e. which using clauses occur), and this context information is not currently kept as part of the payload.
  • Anonymous functions (i.e. lambda expressions) cannot appear as arguments to a dynamic method call. The compiler cannot bind (i.e. “understand”) an anonymous function without knowing what type it is converted to.
  • DLR允許從一個表示類的物件建立物件。然而,C#的當前實現還不具備支援這一功能的語法。
  • 動態查詢不能查詢擴充套件方法。不論擴充套件方法是否依賴該呼叫的靜態上下文(也就是出現了using語句),因為該上下文資訊並不會作為有效載荷的一部分保留下來。
  • 匿名函式(也就是lambda表示式)不能作為實參傳遞給動態方法呼叫。在不知道要轉換成什麼型別的情況下,編譯器不能繫結(也就是“理解”)一個匿名函式。

One consequence of these limitations is that you cannot easily use LINQ queries over dynamic objects:

這些限制導致的結果就是很難在動態物件上使用LINQ查詢——

view plain print ?
  1. dynamic collection = ...;  
  2. var result = collection.Select(e => e + 5);  

If the Select method is an extension method, dynamic lookup will not find it. Even if it is an instance method, the above does not compile, because a lambda expression cannot be passed as an argument to a dynamic operation.

如果Selected 方法是個擴充套件方法,動態查詢將找不到它。即便它是一個例項方法,上面的程式碼也無法編譯,因為lambda表示式不能作為引數傳遞給動態操作。

There are no plans to address these limitations in C# 4.0.

在C# 4.0中沒有計劃解決這些限制。

Named and Optional Arguments
命名引數和可選引數

Named and optional parameters are really two distinct features, but are often useful together. Optional parameters allow you to omit arguments to member invocations, whereas named arguments is a way to provide an argument using the name of the corresponding parameter instead of relying on its position in the parameter list.

命名引數和可選引數是兩個截然不同的功能,但通常一起使用。在進行成員呼叫時,可以忽略可選引數;而命名引數的方式可以通過名稱來提供一個引數,而無需依賴它在引數列表中出現的位置。

Some APIs, most notably COM interfaces such as the Office automation APIs, are written specifically with named and optional parameters in mind. Up until now it has been very painful to call into these APIs from C#, with sometimes as many as thirty arguments having to be explicitly passed, most of which have reasonable default values and could be omitted.

有些API——尤其是COM介面——如Office自動化API——確實本身就是通過命名引數和可選引數編寫的。之前在C#中呼叫這些API非常痛苦,尤其有的時候需要多達30幾個引數都必須顯式傳遞,而其中大多數都具有合理的預設值,是可以忽略的。

Even in APIs for .NET however you sometimes find yourself compelled to write many overloads of a method with different combinations of parameters, in order to provide maximum usability to the callers. Optional parameters are a useful alternative for these situations.

即便是編寫.NET中的API,你也會發現很多時候你在被迫為不同的引數組合方式編寫一個方法的大量過載形式,以便給呼叫者提供最高的可用性。在這種情況下,可選引數就會成為一種非常有用的替代方式。

Optional parameters
可選引數

A parameter is declared optional simply by providing a default value for it:

為一個引數提供預設值就可以將其宣告為可選的——

view plain print ?
  1. public void  M( int  x,  int  y = 5,  int  z = 7);  

Here y and z are optional parameters and can be omitted in calls:

這裡的yz 就是可選引數,在呼叫時可以忽略——

view plain print ?
  1. M(1, 2, 3);  // ordinary call of M
  2. M(1, 2); // omitting z – equivalent to M(1, 2, 7)
  3. M(1); // omitting both y and z – equivalent to M(1, 5, 7)

Named and optional arguments
命名的和可選的實參

C# 4.0 does not permit you to omit arguments between commas as in M(1,,3) . This could lead to highly unreadable comma-counting code. Instead any argument can be passed by name. Thus if you want to omit only y from a call of M you can write:

C# 4.0不允許忽略逗號之間的實參,比如M(1,,3) 。否則會導致大量不可讀的、需要“數逗號”的程式碼。替代方式是任何引數都可以通過名字傳遞。因此如果在呼叫M 時只希望忽略y ,可以寫——

view plain print ?
  1. M(1, z: 3);  // passing z by name

or

view plain print ?
  1. M(x: 1, z: 3);  // passing both x and z by name

or even

甚至

view plain print ?
  1. M(z: 3, x: 1);  // reversing the order of arguments

All forms are equivalent, except that arguments are always evaluated in the order they appear, so in the last example the 3 is evaluated before the 1 .

這幾種形式都是等價的,不過引數總是按照其出現的順序進行求值,因此對於最後一個示例來說,3 會在1 之前求值。

Optional and named arguments can be used not only with methods but also with indexers and constructors.

可選引數和命名引數不僅可以用在方法呼叫中,還可以用在索引器和構造器中。

Overload resolution
過載解析

Named and optional arguments affect overload resolution, but the changes are relatively simple:

命名引數和可選引數影響了過載解析,但產生的變化相當簡單——

A signature is applicable if all its parameters are either optional or have exactly one corresponding argument (by name or position) in the call which is convertible to the parameter type.

如果所有的引數或者是可選的,或者在呼叫時(通過名字或位置)明確提供了對應的實參,並且實參能夠轉換為形參型別,則該簽名是可適用的applicable )。

Betterness rules on conversions are only applied for arguments that are explicitly given – omitted optional arguments are ignored for betterness purposes.

轉換的最優原則只用於明確給定的實參——出於最優的目的,忽略掉的可選引數在過載解析時將不做考慮。

If two signatures are equally good, one that does not omit optional parameters is preferred.

如果兩個簽名一樣好,則沒有忽略可選引數的那個勝出。

view plain print ?
  1. M( string  s,  int  i = 1);  
  2. M(object  o);  
  3. M(int  i,  string  s = “Hello”);  
  4. M(int  i);  
  5. M(5);  

Given these overloads, we can see the working of the rules above. M(string,int) is not applicable because 5 doesn’t convert to string . M(int,string) is applicable because its second parameter is optional, and so, obviously are M(object) and M(int) .

對於給定的這些過載,我們可以看看上述規則的工作方式。M(string,int) 不是可適用的,因為5 不能轉換為stringM(int,string) 是可適用的,因為它的第二個引數是可選的,然後很明顯,M(object)M(int) 也是可適用的。

M(int,string) and M(int) are both better than M(object) because the conversion from 5 to int is better than the conversion from 5 to object .

M(int,string)M(int) 都比M(object) 要好,因為將5 轉換為int 優於將5 轉換為object

Finally M(int) is better than M(int,string) because no optional arguments are omitted.

最後,M(int) 優於M(int,string) ,因為它沒有被忽略的可選引數。

Thus the method that gets called is M(int) .

因此,最終呼叫的方法是M(int)

Features for COM interop
COM互操作特性

Dynamic lookup as well as named and optional parameters greatly improve the experience of interoperating with COM APIs such as the Office Automation APIs. In order to remove even more of the speed bumps, a couple of small COM-specific features are also added to C# 4.0.

動態查詢以及命名引數和可選引數極大地改善了與COM API——如Office Automation API——互操作的體驗。為了減少更多的速度損失,C# 4.0還添加了大量特定於COM的小特性。

Dynamic import
動態匯入

Many COM methods accept and return variant types, which are represented in the PIAs as object. In the vast majority of cases, a programmer calling these methods already knows the static type of a returned object from context, but explicitly has to perform a cast on the returned value to make use of that knowledge. These casts are so common that they constitute a major nuisance.

很多COM方法接受並返回可變型別,這在PIA中會表現為object。在絕大多數情況下,程式設計師在呼叫這些方法之前就已經從上下文中知道了一個返回值物件的靜態型別,但為了使用這些知識,必須明確地在返回值上進行型別轉換。這些轉換非常普遍,帶來了巨大的麻煩。

In order to facilitate a smoother experience, you can now choose to import these COM APIs in such a way that variants are instead represented using the type dynamic . In other words, from your point of view, COM signatures now have occurrences of dynamic instead of object in them.

為了得到無縫體驗,現在你可以選擇使用dynamic 型別來代替可變型別的方式。換句話說,從你的角度來看,COM簽名中出現的是dynamic 而不是object

This means that you can easily access members directly off a returned object, or you can assign it to a strongly typed local variable without having to cast. To illustrate, you can now say

這意味著你可以直接在返回的物件上訪問成員,或者可以使用強型別的區域性變數為其賦值,而無需進行轉換。例如,你可以寫

view plain print ?
  1. excel.Cells[1, 1].Value =  "Hello" ;  

instead of

而不用寫

view plain print ?
  1. ((Excel.Range)excel.Cells[1, 1]).Value2 =  "Hello" ;  

and

又如

view plain print ?
  1. Excel.Range range = excel.Cells[1, 1];  

instead of

而不用寫

view plain print ?
  1. Excel.Range range = (Excel.Range)excel.Cells[1, 1];  

Compiling without PIAs
無PIA的編譯

Primary Interop Assemblies are large .NET assemblies generated from COM interfaces to facilitate strongly typed interoperability. They provide great support at design time, where your experience of the interop is as good as if the types where really defined in .NET. However, at runtime these large assemblies can easily bloat your program, and also cause versioning issues because they are distributed independently of your application.

主互操作程式集(Primary Interop Assembly)是從COM介面生成的大型.NET程式集,用於協助完成強型別的互操作。它們為設計時提供了巨大的支援,就好像其中的型別真的是 用.NET定義的一樣。然而,在執行時這些大型程式集很容易使你的程式膨脹起來,而且很容易導致版本問題,因為它們是分散式的,不依賴你的應用程式。

The no-PIA feature allows you to continue to use PIAs at design time without having them around at runtime. Instead, the C# compiler will bake the small part of the PIA that a program actually uses directly into its assembly. At runtime the PIA does not have to be loaded.

無PIA特性允許你繼續在設計時使用PIA,而無需在執行時使用它們。C#編譯器會將程式中實際用到的PIA中的一小部分直接編譯到程式集中。在執行時無需載入PIA。

Omitting ref
省略ref

Because of a different programming model, many COM APIs contain a lot of reference parameters. Contrary to ref s in C#, these are typically not meant to mutate a passed-in argument for the subsequent benefit of the caller, but are simply another way of passing value parameters.

由於採用了不同的程式設計模型,很多COM API包含大量的引用引數。與C#中的ref 相反,這些引數並不意味著要修改傳入的實參以供呼叫方之後使用,而只是另外一種傳遞引數值的簡單方式。

It therefore seems unreasonable that a C# programmer should have to create temporary variables for all such ref parameters and pass these by reference. Instead, specifically for COM methods, the C# compiler will allow you to pass arguments by value to such a method, and will automatically generate temporary variables to hold the passed-in values, subsequently discarding these when the call returns. In this way the caller sees value semantics, and will not experience any side effects, but the called method still gets a reference.

C#程式設計師必須為所有這些ref 引數建立臨 時變數,並按引用進行傳遞,這看上去一點也不合理。因此,對於COM方法,C#編譯器允許按值傳遞這些引數,並自動生成存放傳入值的臨時變數,並在呼叫返 回後丟棄這些變數。使用這種方式,呼叫方看到的語義是按值傳遞,而且不會有任何副作用,而被呼叫的方法得到的依然是一個引用。

Open issues
已知問題

A few COM interface features still are not surfaced in C#. Most notably these include indexed properties and default properties. As mentioned above these will be respected if you access COM dynamically, but statically typed C# code will still not recognize them.

一小部分COM介面特性沒有出現在C#中。尤其是索引屬性和預設屬性。如果是動態訪問COM,可以用之前提到的特性來解決,但靜態型別的C#程式碼仍然無法識別它們。

There are currently no plans to address these remaining speed bumps in C# 4.0.

在C# 4.0中沒有計劃解決這些剩餘的速度損失。

Variance
變性

An aspect of generics that often comes across as surprising is that the following is illegal:

泛型的某個方面會讓人感到奇怪,比如下面的程式碼是不合法的——

view plain print ?
  1. IList< string > strings =  new  List< string >();  
  2. IList<object > objects = strings;  

The second assignment is disallowed because strings does not have the same element type as objects . There is a perfectly good reason for this. If it were allowed you could write:

第二個賦值是不允許的,因為stringsobjects 的元素型別並不一樣。這樣做有這充分的原因。如果允許那樣寫的話,你可能會寫——

view plain print ?
  1. objects[0] = 5;  
  2. string  s = strings[0];  

Allowing an int to be inserted into a list of strings and subsequently extracted as a string . This would be a breach of type safety.

這會允許將int 插入strings 列表中,然後將其作為string 取出。這會破壞型別安全。

However, there are certain interfaces where the above cannot occur, notably where there is no way to insert an object into the collection. Such an interface is IEnumerable<T> . If instead you say:

然而,對於某些介面來說上述情況並不會發生,尤其是不能將物件插入集合時。例如IEnumerable<T> 就是這樣的介面。如果改為——

view plain print ?
  1. IEnumerable< object > objects = strings;  

There is no way we can put the wrong kind of thing into strings through objects , because objects doesn’t have a method that takes an element in. Variance is about allowing assignments such as this in cases where it is safe. The result is that a lot of situations that were previously surprising now just work.

這樣就沒法通過objects 將錯誤型別的東西插入到strings 中了,因為objects 沒有插入元素的方法。變性(variance)就是用於在這種能保證安全的情況下進行賦值的。結果就是很多之前讓我們感到奇怪的情況現在可以工作了。

Covariance
協變性

In .NET 4.0 the IEnumerable<T> interface will be declared in the following way:

在.NET 4.0中,IEnumerable<T> 介面將會按照下面的方式進行定義——

view plain print ?
  1. public interface  IEnumerable< out  T> : IEnumerable  
  2. {  
  3.         IEnumerator<T> GetEnumerator();  
  4. }  
  5. public interface  IEnumerator< out  T> : IEnumerator  
  6. {  
  7.         bool  MoveNext();  
  8.         T Current { get ; }  
  9. }  

The “out ” in these declarations signifies that the T can only occur in output position in the interface – the compiler will complain otherwise. In return for this restriction, the interface becomes “covariant” in T , which means that an IEnumerable<A> is considered an IEnumerable<B> if A has a reference conversion to B .

這些宣告中的“out ”指出T 只能出現在介面的輸出位置——如果不是這樣的話,編譯器會報錯。有了這一限制,介面對於T 型別就是“協變的”,這意味著如果A 可以按引用轉換為B ,則IEnumerable<A> 可以當作IEnumerable<B> 使用。

As a result, any sequence of strings is also e.g. a sequence of objects.

其結果是,任何一個字串序列也就是一個物件序列了。

This is useful e.g. in many LINQ methods. Using the declarations above:

這很有用,例如在LINQ方法中。使用上面的定義——

view plain print ?
  1. var result = strings.Union(objects);  // succeeds with an IEnumerable<object>

This would previously have been disallowed, and you would have had to to some cumbersome wrapping to get the two sequences to have the same element type.

之前這樣做是不允許的,你必須做一些麻煩的包裝,使得兩個序列具有相同的元素型別。

Contravariance
逆變性

Type parameters can also have an “in ” modifier, restricting them to occur only in input positions. An example is IComparer<T> :

型別引數還可以具有“in ”修飾符,限制它們只能出現在輸入位置上。例如IComparer<T> ——

view plain print ?
  1. public interface  IComparer< in  T>  
  2. {  
  3.         public int  Compare(T left, T right);  
  4. }  

The somewhat baffling result is that an IComparer<object> can in fact be considered an IComparer<string> ! It makes sense when you think about it: If a comparer can compare any two object s, it can certainly also compare two string s. This property is referred to as contravariance.

其結果有點讓人迷惑,就是IComparer<object> 可以作為IComparer<string> 使用!這樣考慮這個結果就會很有意義——如果一個比較器可以比較任意 兩個object ,它當然也可以比較兩個string 。這種性質被稱作“逆變性(contravariance)”。

A generic type can have both in and out modifiers on its type parameters, as is the case with the Func<...> delegate types:

泛型型別可以同時擁有帶有inout 修飾符的型別引數,例如Func<...> 委託型別——

view plain print ?
  1. public delegate  TResult Func< in  TArg,  out  TResult>(TArg arg);  

Obviously the argument only ever comes in , and the result only ever comes out . Therefore a Func<object,string> can in fact be used as a Func<string,object> .

很明顯引數永遠都是傳 的,而結果永遠只能是傳 的。因此,Func<object, string> 可以用作Func<string, object>

Limitations
限制

Variant type parameters can only be declared on interfaces and delegate types, due to a restriction in the CLR. Variance only applies when there is a reference conversion between the type arguments. For instance, an IEnumerable<int> is not an IEnumerable<object> because the conversion from int to object is a boxing conversion, not a reference conversion.

變性型別引數只能在介面和委託型別中宣告,這是CLR的限制。變性只能應用在型別引數的按引用 轉換之間。例如,IEnumerable<int> 不能作為IEnumerable<object> 使用,因為從intobject 的轉換是裝箱轉換,而不是引用轉換。

Also please note that the CTP does not contain the new versions of the .NET types mentioned above. In order to experiment with variance you have to declare your own variant interfaces and delegate types.

還要注意的是,CTP中並沒有包含前面提到的.NET型別的新版本。為了試驗變性,你需要自己宣告變性介面和委託型別。

COM Example
COM示例

Here is a larger Office automation example that shows many of the new C# features in action.

這裡有一個稍大一些的Office自動化示例,展示了大部分C#新特性的實際應用。

view plain print ?
  1. using  System;  
  2. using  System.Diagnostics;  
  3. using  System.Linq;  
  4. using  Excel = Microsoft.Office.Interop.Excel;  
  5. using  Word = Microsoft.Office.Interop.Word;  
  6. class  Program  
  7. {  
  8.     static void  Main( string [] args) {  
  9.         var excel = new  Excel.Application();  
  10.         excel.Visible = true ;  
  11.         excel.Workbooks.Add();                    // optional arguments omitted
  12.         excel.Cells[1, 1].Value = "Process Name" // no casts; Value dynamically  
  13.         excel.Cells[1, 2].Value = "Memory Usage" // accessed
  14.         var processes = Process.GetProcesses()  
  15.             .OrderByDescending(p => p.WorkingSet)  
  16.             .Take(10);  
  17.         int  i = 2;  
  18.         foreach  (var p  in  processes) {  
  19.             excel.Cells[i, 1].Value = p.ProcessName; // no casts
  20.             excel.Cells[i,