C# 4 中的 Dynamic 關鍵字

      網友投稿 837 2025-04-01

      轉自 https://kb.cnblogs.com/page/92947/

      dynamic 關鍵字和動態(tài)語言運行時 (DLR) 是 C# 4 和 Microsoft .NET Framework 4 中的重大新增功能。 這些功能在宣布時就引起了人們的極大興趣,并伴隨著許多疑問。 同時人們也給出了很多答案,但這些答案現(xiàn)在已散布于各種文檔以及各種技術博客和文章之中。 這樣,人們在各種論壇和會議上總是一遍又一遍地提出相同的問題。

      本文全面概述了 C# 4 中新增的動態(tài)功能,并且深入探討了這些功能如何同其他語言和框架功能(例如反射或隱式類型化變量)一起使用。 鑒于已有大量信息可用,我有時會重新使用一些經典示例,并提供指向原始源的鏈接。 我還將提供指向相關內容的大量鏈接,供您進一步閱讀。

      什么是“動態(tài)”?

      編程語言有時可劃分為靜態(tài)類型化語言和動態(tài)類型化語言。 C# 和 Java 經常被認為是靜態(tài)類型化語言的例子,而 Python、Ruby 和 JavaScript 是動態(tài)類型化語言的例子。

      一般而言,動態(tài)語言不執(zhí)行編譯時類型檢查,僅在運行時識別對象的類型。 這種方法有利有弊:代碼編寫起來往往更快、更容易,但同時,由于您不會獲得編譯器錯誤,只能通過單元測試和其他方法來確保應用程序正常運行。

      C# 最初是作為純靜態(tài)語言創(chuàng)建的,但 C# 4 添加了一些動態(tài)元素,用以改進與動態(tài)語言和框架之間的互操作性。 C# 團隊考慮了多種設計選項,但最終確定添加一個新關鍵字來支持這些功能:dynamic。

      dynamic 關鍵字可充當 C# 類型系統(tǒng)中的靜態(tài)類型聲明。 這樣,C# 就獲得了動態(tài)功能,同時仍然作為靜態(tài)類型化語言而存在。 若要了解為何以及如何做出了這樣的決定,請參考 PDC09 (microsoftpdc.com/2009/FT31) 上由Mads Torgersen 撰寫的演示文稿“C# 4 中的動態(tài)綁定”。 尤其是,動態(tài)對象被認定是 C# 語言中的“一等公民”,因此沒有用于打開或關閉動態(tài)功能的選項,并且沒有向 C# 添加過類似于 Visual Basic 中的 Option Strict On/Off 之類的功能。

      當您使用 dynamic 關鍵字時,您就告訴編譯器關閉編譯時檢查。 網上以及 MSDN 文檔中 (msdn.microsoft.com/library/dd264736) 有大量關于如何使用該關鍵字的示例。 下面是一個常見示例:

      dynamic d = "test"; Console.WriteLine(d.GetType()); // Prints "System.String". d = 100; Console.WriteLine(d.GetType()); // Prints "System.Int32".

      1

      2

      3

      4

      5

      6

      如您所見,可以將不同類型的對象分配給已聲明為 dynamic 的變量。 這段代碼會通過編譯,并在運行時確定對象的類型。 不過,下面的代碼也會通過編譯,但在運行時會引發(fā)異常:

      dynamic d = "test"; // The following line throws an exception at run time. d++;

      1

      2

      3

      原因是相同的:編譯器不知道該對象的運行時類型,因此無法告訴您遞增操作在此情況下不受支持。

      缺少編譯時類型檢查也會導致 IntelliSense 功能無效。 由于 C# 編譯器不知道對象的類型,因此它無法枚舉該對象的屬性和方法。 正如在用于 Visual Studio 的 IronPython 工具中那樣,通過附加的類型推斷可能會解決此問題,但目前 C# 不提供這種類型推斷。

      但是,在許多可能獲益于動態(tài)功能的方案中,由于代碼使用了字符串文本而導致 IntelliSense 還是不可用。 本文在后面將對這一問題進行更詳細的討論。

      Dynamic、Object 還是 Var?

      那么,dynamic、object 和 var 之間的實際區(qū)別是什么?何時應使用它們? 下面是每個關鍵字的簡短定義和一些示例。

      關鍵字 object 表示 System.Object 類型,它是 C# 類層次結構中的根類型。 此關鍵字經常在編譯時無法確定對象類型時使用,而這種情況經常在各種互操作性情形中發(fā)生。

      您需要使用顯式轉換將已聲明為 object 的變量轉換為特定類型:

      object objExample = 10; Console.WriteLine(objExample.GetType());

      1

      2

      顯然,這將輸出 System.Int32。 但是,因為靜態(tài)類型為 System.Object,所以您在這里需要一個顯式轉換:

      objExample = (int)objExample + 10;

      1

      您可以賦予不同類型的值,因為它們都是從 System.Object 繼承的:

      objExample = "test"

      1

      從 C# 3.0 起,關鍵字 var 開始用于隱式類型化局部變量以及匿名類型。 此關鍵字經常與 LINQ 結合使用。 當使用 var 關鍵字聲明變量時,將在編譯時根據(jù)初始化字符串推斷該變量的類型。 在運行時無法更改該變量的類型。 如果編譯器不能推斷類型,它會生成一個編譯錯誤:

      var varExample = 10; Console.WriteLine(varExample.GetType());

      1

      2

      這段代碼會輸出 System.Int32,與靜態(tài)類型相同。

      在下面的示例中,因為 varExample 的靜態(tài)類型為 System.Int32,所以不需要轉換:

      varExample = varExample + 10;

      1

      下面一行不進行編譯,因為只能將整數(shù)賦給 varExample:

      varExample = "test";

      1

      C# 4 中引入的 dynamic 關鍵字可使某些傳統(tǒng)上依賴于 object 關鍵字的情形更容易編寫和維護。 實際上,動態(tài)類型在后臺使用 System.Object 類型。但與 object 不同的是,動態(tài)類型不需要在編譯時執(zhí)行顯式轉換操作,因為它僅在運行時識別類型:

      dynamic dynamicExample = 10; Console.WriteLine(dynamicExample.GetType());

      1

      2

      此段代碼會輸出 System.Int32。

      在下面這一行中不需要轉換,因為僅在運行時識別類型:

      dynamicExample = dynamicExample + 10;

      1

      可以將不同類型的值賦給 dynamicExample:

      dynamicExample = "test";

      1

      在 C# 常見問題解答博客 (bit.ly/c95hpl) 上,提供了關于關鍵字 object 和 dynamic 之間差別的詳細博客文章。

      有時會引起混淆的是,所有這些關鍵字可以一起使用,即它們不是互相排斥的。 例如,我們來看一看下面的代碼:

      dynamic dynamicObject = new Object(); var anotherObject = dynamicObject;

      1

      2

      anotherObject 的類型是什么? 我的回答是:dynamic。 請記住,在 C# 類型系統(tǒng)中,dynamic 實際上是一個靜態(tài)類型,因此,編譯器將為 anotherObject 推斷此類型。 務必要知道,var 關鍵字不過是一個指令,它讓編譯器根據(jù)變量的初始化表達式推斷類型;var 不是類型。

      動態(tài)語言運行時

      說起 C# 語言環(huán)境中的“dynamic”這一術語,它通常指下面兩個概念之一:C# 4 中的 dynamic 關鍵字或 DLR。 雖然這兩個概念是相關的,但也務必要了解它們之間的差別。

      DLR 有兩個主要目的。 首先,它實現(xiàn)動態(tài)語言和 .NET Framework 之間的互操作。 其次,它將動態(tài)行為引入 C# 和 Visual Basic 之中。

      DLR 的創(chuàng)建吸取了構建 IronPython (ironpython.net) 時的經驗教訓(IronPython 是在 .NET Framework 上實現(xiàn)的第一種動態(tài)語言)。 在構建 IronPython 時,工作團隊發(fā)現(xiàn)他們可以針對多種語言重復使用他們的實現(xiàn),因此,他們?yōu)?.NET 動態(tài)語言創(chuàng)建了一個公共基礎平臺。 與 IronPython 一樣,DLR 已成為一個開源項目,其源代碼目前在 dlr.codeplex.com 上提供。

      后來,.NET Framework 4 中也納入了 DLR,以支持 C# 和 Visual Basic 中的動態(tài)功能。 如果您只需要 C# 4 中的 dynamic 關鍵字,那么使用 .NET Framework 就可以了。在大多數(shù)情況下,僅憑 .NET Framework 即可處理與 DLR 之間的所有交互。 但是,如果您希望實現(xiàn)新的動態(tài)語言或將其遷移到 .NET,則可以獲益于開源項目中額外的幫助程序類,該開源項目為語言實現(xiàn)人員提供了更多功能和服務。

      在靜態(tài)類型化語言中使用 Dynamic

      我們并不期待每個人都盡可能使用動態(tài)而不是靜態(tài)類型聲明。 編譯時檢查是一個強大的工具,對它的使用多多益善。 而且,再次指出,C# 中的動態(tài)對象不支持 IntelliSense,這對總體工作效率可能會有些影響。

      同時,在出現(xiàn) dynamic 關鍵字和 DLR 之前,有一些方案在 C# 中曾經難以實現(xiàn)。 在以前的大多數(shù)情況下,開發(fā)人員使用 System.Object 類型和顯式轉換,同樣不能很好地利用編譯時檢查和 IntelliSense。 下面是一些例子。

      人們最熟知的一個情況是,有時必須使用 object 關鍵字來實現(xiàn)與其他語言或框架的互操作性。 通常,您必須依靠反射來獲取對象的類型以及訪問其屬性和方法。 語法有時難以閱讀,因此代碼難以維護。 此時使用動態(tài)功能可能比使用反射更加容易和方便。

      Anders Hejlsberg 在 PDC08 (channel9.msdn.com/pdc2008/TL16) 上提供了一個極好的例子,如下所示:

      object calc = GetCalculator(); Type calcType = calc.GetType(); object res = calcType.InvokeMember( "Add", BindingFlags.InvokeMethod, null, new object[] { 10, 20 }); int sum = Convert.ToInt32(res);

      1

      2

      3

      4

      5

      6

      該函數(shù)返回一個計算器,但系統(tǒng)在編譯時不知道此計算器對象的精確類型。 代碼所依賴的唯一事情是此對象應具有 Add 方法。 請注意,此方法無法使用 IntelliSense,因為您以字符串文本的形式提供了方法名稱。

      使用 dynamic 關鍵字,代碼就很簡單了:

      dynamic calc = GetCalculator(); int sum = calc.Add(10, 20);

      1

      2

      假設情況沒有變化:存在某種我們希望其具有 Add 方法的未知類型的對象。 與上一個示例一樣,此方法也不能使用 IntelliSense。 但語法閱讀和使用起來要容易很多,看上去就像在調用一個普通的 .NET 方法。

      動態(tài)方法包

      可以利用動態(tài)功能的另外一個例子是創(chuàng)建動態(tài)方法包,動態(tài)方法包就是可在運行時添加和刪除屬性及方法的對象。

      .NET Framework 4 有一個新的命名空間:System.Dynamic。 此命名空間實際上是 DLR 的一部分。 System.Dynamic.ExpandoObject 和 System.Expando.DynamicObject 類與新的 dynamic 關鍵字相結合,有助于以清晰和易于閱讀的方式來創(chuàng)建動態(tài)結構和層次結構。

      例如,下面說明了如何使用 ExpandoObject 類來添加屬性和方法:

      dynamic expando = new ExpandoObject(); expando.SampleProperty = "This property was added at run time"; expando.SampleMethod = (Action)(() => Console.WriteLine(expando.SampleProperty)); expando.SampleMethod();

      1

      2

      3

      4

      要了解更加深入的方案,您一定要看看關于 ExpandoObject 和 DynamicObject 類的 MSDN 文檔。 同時,還有一些值得一看的文章,比如由 Bill Wagner 撰寫的文章“動態(tài)方法包”(msdn.microsoft.com/library/ee658247) 以及 C# 常見問題解答博客文章“C# 4.0 中的 Dynamic:ExpandoObject 簡介”(bit.ly/amRYRw)。

      類包裝

      您可以為自己的庫提供更好的語法,或為現(xiàn)有庫創(chuàng)建包裝。 與前兩個方案相比,這是一個更高級的方案,并且需要對 DLR 具體內容有更深入的了解。

      對于簡單情況,可以使用 DynamicObject 類。 在這個類中,可以將方法和屬性的靜態(tài)聲明與動態(tài)調度進行混合。 這樣,您就可以在一個類屬性中存儲一個要為其提供更佳語法的對象,但通過動態(tài)調度來處理針對該對象的所有操作。

      例如,請看一下圖 1 中的 DynamicString 類,該類包裝了一個字符串,并在通過反射實際調用所有方法之前顯示這些方法的名稱。

      public class DynamicString : DynamicObject { string str; public DynamicString(string str) { this.str = str; } public override bool TryInvokeMember( InvokeMemberBinder binder, object[] args, out object result) { Console.WriteLine("Calling method: {0}", binder.Name); try { result = typeof(string).InvokeMember( binder.Name, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, str, args); return true; } catch { result = null; return false; } } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      若要實例化該類,應使用 dynamic 關鍵字:

      dynamic dStr = new DynamicString(“Test”);

      Console.WriteLine(dStr.ToUpper());

      Console.ReadLine();

      當然,這個特定示例出于演示目的而設計,不具有實際效率。 但是,如果您擁有已嚴重依賴于反射的 API,就可以如此處所示將所有通過反射進行的調用打包,以便針對 API 的最終用戶隱藏這些調用。

      有關更多示例,請參見 MSDN 文檔 (msdn.microsoft.com/library/system.dynamic.dynamicobject) 以及 C# 常見問題解答博客文章“C# 4.0 中的 Dynamic:通過 DynamicObject 創(chuàng)建包裝”(bit.ly/dgS3od)。

      可編寫腳本的應用程序

      腳本是向應用程序提供可擴展性的一種強大方法。 Microsoft Office 可作為這方面的一個好例子:由于 Visual Basic for Applications (VBA) 的存在,可以使用大量的宏、加載項和插件。 現(xiàn)在,DLR 提供了一組公用的語言宿主 API,因此可讓您創(chuàng)建可編寫腳本的應用程序。

      例如,您可以創(chuàng)建一個應用程序,使用戶能夠自己在其中添加功能而不需要主產品提供新功能,例如向游戲中添加新的字符和映射,或向業(yè)務應用程序添加新的圖表。

      您必須使用來自 dlr.codeplex.com 的開源版 DLR 而不是由 .NET Framework 4 使用的 DLR,因為 DLR 腳本編寫和宿主 API 現(xiàn)在僅在開源版中提供。 另外,假定的情況是您不是使用 C# 編寫腳本,而是使用一種 .NET 動態(tài)語言(如 IronPython 或 IronRuby)來編寫。 然而,實際上任何語言都可以支持這些 API,包括不是在 DLR 之上實現(xiàn)的語言。

      有關使用此功能的詳細信息,請觀看 PDC09 (microsoftpdc.com/2009/FT30) 上由 Dino Viehland 所做的演示“使用動態(tài)語言生成可編寫腳本的應用程序”。

      識別動態(tài)對象

      如何區(qū)分動態(tài)對象與其他對象? 一個簡便方法是使用內置的 IDE 功能。 您可以將鼠標光標懸停在對象上以查看其聲明類型,或檢查 IntelliSense 是否可用。

      然而在運行時,情況會變得更加復雜。 您無法檢查變量是否是通過 dynamic 關鍵字聲明的 — 動態(tài)對象的運行時類型是它所存儲的值的類型,您無法獲取其靜態(tài)類型聲明。 這種情況與將變量聲明為 object 時的情況相同:在運行時,您只能獲取變量所存儲的值的類型;無法判斷此變量最初是否聲明為 object。

      運行時所能確定的是對象是否來自 DLR。 知道這種情況可能十分重要,因為像 ExpandoObject 和 DynamicObject 類型的對象可在運行時改變其行為,例如,添加和刪除屬性及方法。

      此外,也無法使用標準反射方法來獲取有關這些對象的信息。 如果向 ExpandoObject 類的實例添加屬性,則無法通過反射獲取該屬性:

      dynamic expando = new ExpandoObject(); expando.SampleProperty = "This property was added at run time"; PropertyInfo dynamicProperty = expando.GetType().GetProperty("SampleProperty"); // dynamicProperty is null.

      1

      2

      3

      4

      C# 4 中的 Dynamic 關鍵字

      有利的方面是,在 .NET Framework 4 中,所有可動態(tài)添加和刪除成員的對象都必須實現(xiàn)一個特定接口:System.Dynamic.IDynamicMetaObjectProvider。 DynamicObject 和 ExpandoObject 類也實現(xiàn)了這個接口。 不過,這并不表示任何使用 dynamic 關鍵字聲明的對象都實現(xiàn)此接口:

      dynamic expando = new ExpandoObject(); Console.WriteLine(expando is IDynamicMetaObjectProvider); // True dynamic test = "test"; Console.WriteLine(test is IDynamicMetaObjectProvider); // False

      1

      2

      3

      4

      5

      6

      因此,如果將動態(tài)功能與反射一起使用,則請記住,反射不適用于動態(tài)添加的屬性和方法,并且最好檢查正在反射的對象是否實現(xiàn)了 IDynamicMetaObjectProvider 接口。

      動態(tài)功能與 COM 互操作

      C# 團隊在 C# 4 版本中專門考慮的 COM 互操作方案是針對 Microsoft Office 應用程序(如 Word 和 Excel)進行編程。 他們的目的是讓這一任務在 C# 中變得像在 Visual Basic 中那樣容易和自然。 這也是 Visual Basic 和 C# 共同發(fā)展策略的一部分,這個策略旨在實現(xiàn)兩種語言的功能對等,并相互借鑒最佳、最具效率的解決方案。

      若需了解詳細信息,請參閱 Scott Wiltamuth 的 Visual Studio 博客文章“C# 和 VB 共同發(fā)展”(bit.ly/bFUpxG)。

      顯示了一段 C# 4 代碼,該代碼向 Excel 工作表的第一個單元格中添加一個值,然后向第一列應用 AutoFit 方法。 每行下面的注釋顯示了 C# 3.0 及更早版本的中的等效代碼。

      // Add this line to the beginning of the file: // using Excel = Microsoft.Office.Interop.Excel; var excelApp = new Excel.Application(); excelApp.Workbooks.Add(); // excelApp.Workbooks.Add(Type.Missing); excelApp.Visible = true; Excel.Range targetRange = excelApp.Range["A1"]; // Excel.Range targetRange = excelApp.get_Range("A1", Type.Missing); targetRange.Value = "Name"; // targetRange.set_Value(Type.Missing, "Name"); targetRange.Columns[1].AutoFit(); // ((Excel.Range)targetRange.Columns[1, Type.Missing]).AutoFit();

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      此示例有趣的地方是,您在代碼中的任何位置都看不到 dynamic 關鍵字。實際上,該關鍵字僅在下面一行中用到:

      targetRange.Columns[1].AutoFit(); // ((Excel.Range)targetRange.Columns[1, Type.Missing]).AutoFit();

      1

      2

      在 C# 3.0 版中,targetRange.Columns[1, Type.Missing] 返回 object,這便是需要向 Excel.Range 轉換的原因。 但在 C# 4 和 Visual Studio 2010 中,這樣的調用將以靜默方式轉換為動態(tài)調用。 因此,C# 4 中 targetRange.Columns[1] 的類型實際上是 dynamic。

      另一個突出特點是,C# 4 中的 COM 互操作改進不僅限于 dynamic。 由于其他一些新增功能(例如索引屬性以及命名參數(shù)和可選參數(shù)),其他所有行中的代碼也有所改進。 由 Chris Burrows 撰寫的 MSDN 雜志 文章“.NET Framework 4 中的新增 C# 功能”(msdn.microsoft.com/magazine/ff796223) 中對這些新增功能做了很好的概述。

      .NET C#

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:下劃線在excel怎么打出來(excel中如何打出下劃線)
      下一篇:# 鴻蒙內核M核源碼分析系列二一 02 文件系統(tǒng)LittleFS
      相關文章
      亚洲成a人片在线观看老师| 久久91亚洲人成电影网站| 国产L精品国产亚洲区久久| 亚洲精品无码日韩国产不卡av| 亚洲精品视频免费看| 青青草原精品国产亚洲av| 亚洲AV无码乱码国产麻豆 | 国产v亚洲v天堂a无| 亚洲嫩草影院在线观看| 亚洲视频小说图片| 亚洲成a人片在线观看中文!!! | 亚洲综合激情视频| 亚洲欧洲高清有无| 亚洲xxxxxx| 亚洲首页国产精品丝袜| 97se亚洲国产综合自在线| 亚洲中文字幕AV在天堂| 在线观看亚洲AV每日更新无码| 亚洲综合精品成人| 亚洲一卡2卡三卡4卡无卡下载 | 亚洲精品无码久久久久去q | 亚洲欧洲国产综合AV无码久久| 亚洲精品乱码久久久久蜜桃| 亚洲av无码成人精品区一本二本| 亚洲av无码成人精品国产| 亚洲AV网站在线观看| 国产成人精品日本亚洲专区| 亚洲熟妇av一区二区三区 | 国产亚洲A∨片在线观看| 亚洲av无码片在线播放| 久久精品国产亚洲AV香蕉| 亚洲午夜国产精品| 亚洲 欧洲 自拍 另类 校园| 亚洲AV色无码乱码在线观看| 亚洲AV无码成人精品区大在线| 国产精品亚洲玖玖玖在线观看 | 亚洲日韩精品国产一区二区三区| 久久精品亚洲日本波多野结衣| 亚洲日韩在线观看免费视频| 亚洲精品自产拍在线观看| 久久伊人久久亚洲综合|