C# 4 引入了一個新類型 dynamic
。 該類型是一種靜態類型,但類型為 dynamic
的對象會跳過靜態類型檢查。 大多數情況下,該對象就像具有類型 object
一樣。 在編譯時,將假定類型化為 dynamic
的元素支持任何操作。 因此,不必考慮對象是從 COM API、從動態語言(例如 IronPython)、從 HTML 文檔對象模型 (DOM)、從反射還是從程序中的其他位置獲取自己的值。 但是,如果代碼無效,則在運行時會捕獲到錯誤。
在通過 dynamic
類型實現的操作中,該類型的作用是繞過編譯時類型檢查。 改為在運行時解析這些操作。 dynamic
類型簡化了對 COM API(例如 Office Automation API)、動態 API(例如 IronPython 庫)和 HTML 文檔對象模型 (DOM) 的訪問。
在大多數情況下,dynamic
類型與 object
類型的行為類似。 但是,如果操作包含 dynamic
類型的表達式,那么不會通過編譯器對該操作進行解析或類型檢查。 編譯器將有關該操作信息打包在一起,之后這些信息會用於在運行時評估操作。 在此過程中,dynamic
類型的變量會編譯為 object
類型的變量。 因此,dynamic
類型只在編譯時存在,在運行時則不存在。
下面的示例將 dynamic
類型的變量與 object
類型的變量進行對比。 若要在編譯時驗證每個變量的類型,請將鼠標指針放在 WriteLine
語句中的 dyn
或 obj
上。 IntelliSense 對 dyn
顯示“dynamic” ,對 obj
顯示“object” 。
class Program { static void Main(string[] args) { dynamic dyn = 1; object obj = 1; System.Console.WriteLine(dyn.GetType()); System.Console.WriteLine(obj.GetType()); } }
輸出結果:
若要查看編譯時 dyn
與 obj
之間的區別,請在前面示例的聲明和 WriteLine
語句之間添加下列兩行:
dyn = dyn + 3; obj = obj + 3;
嘗試在表達式 obj + 3
中添加整數和對象時,將報告編譯器錯誤。 但是,對於 dyn + 3
,不會報告任何錯誤。 在編譯時不會檢查包含 dyn
的表達式,原因是 dyn
的類型為 dynamic
。
dynamic
關鍵字可以直接出現,也可以作為構造類型的組件在下列情況中出現:
-
在聲明中,作為屬性、字段、索引器、參數、返回值、本地變量或類型約束的類型。下面的類定義在多個不同的聲明中使用
dynamic
。
class ExampleClass { // 動態字段 static dynamic field; // 動態屬性 dynamic prop { get; set; } // 動態返回類型和動態類型參數 public dynamic exampleMethod(dynamic d) { // 局部動態變量 dynamic local = "Local variable"; int two = 2; if (d is int) { return local; } else { return two; } } }
-
在顯式類型轉換中,作為轉換的目標類型。
static void convertToDynamic() { dynamic d; int i = 20; d = (dynamic)i; Console.WriteLine(d); string s = "Example string."; d = (dynamic)s; Console.WriteLine(d); DateTime dt = DateTime.Today; d = (dynamic)dt; Console.WriteLine(d); } // 輸出結果: // 20 // Example string. // 7/25/2018 12:00:00 AM
- 在以下任何情況下:類型用作值(如
is
運算符或as
運算符右側),或者用作構造類型中typeof
的參數。 例如,可以在下列表達式中使用dynamic
。
int i = 8; dynamic d; // 使用 is 操作符 // 在此處動態類型和object相似,The dynamic type behaves like object。除非 somevar 的值為 null ,否則以下表達式將返回true if (someVar is dynamic) { } // 使用 as 操作符 d = i as dynamic; // 使用typeof, 作為構造類型的一部分 Console.WriteLine(typeof(List<dynamic>)); // 以下語句導致編譯器錯誤 //Console.WriteLine(typeof(dynamic));
dynamic
。 Main
方法也將編譯時類型檢查與運行時類型檢查進行了對比。
using System; namespace DynamicExamples { class Program { static void Main(string[] args) { ExampleClass ec = new ExampleClass(); Console.WriteLine(ec.exampleMethod(10)); Console.WriteLine(ec.exampleMethod("value")); // 下面的語句會引發編譯器異常。因為 exampleMethod 方法僅包含一個參數 //Console.WriteLine(ec.exampleMethod(10, 4)); dynamic dynamic_ec = new ExampleClass(); Console.WriteLine(dynamic_ec.exampleMethod(10)); // 因為 dynamic_ec 是 dynamic 類型, 下面的調用(傳遞了2個參數)不會引發編譯器異常。 // 但是在運行時會引發異常。 //Console.WriteLine(dynamic_ec.exampleMethod(10, 4)); } } class ExampleClass { static dynamic field; dynamic prop { get; set; } public dynamic exampleMethod(dynamic d) { dynamic local = "Local variable"; int two = 2; if (d is int) { return local; } else { return two; } } } } // 輸出結果: // Local variable // 2 // Local variable
以上示例中,編譯器的作用是將有關每個語句的預期作用的信息一起打包到類型化為 dynamic
的對象或表達式。 在運行時,將對存儲的信息進行檢查,並且任何無效的語句都將導致運行時異常。
大多數動態操作的結果是其本身 dynamic
。 例如,如果將鼠標指針放在以下示例中使用的 testSum
上,則 IntelliSense 將顯示類型“(局部變量)dynamic testSum” 。
dynamic d = 1; var testSum = d + 3; System.Console.WriteLine(testSum);
結果不為 dynamic
的操作包括:
- 從
dynamic
到另一種類型的轉換。 - 包括類型為
dynamic
的自變量的構造函數調用。
例如,以下聲明中 testInstance
的類型為 ExampleClass
,而不是 dynamic
:
var testInstance = new ExampleClass(d);
動態對象和其他類型之間的轉換非常簡單。 這樣,開發人員將能夠在動態行為和非動態行為之間切換。
任何對象都可隱式轉換為動態類型,如以下示例所示。
dynamic d1 = 7; dynamic d2 = "a string"; dynamic d3 = System.DateTime.Today; dynamic d4 = System.Diagnostics.Process.GetProcesses();
反之,隱式轉換也可動態地應用於類型為 dynamic
的任何表達式。
int i = d1; string str = d2; DateTime dt = d3; System.Diagnostics.Process[] procs = d4;
dynamic
,或者方法調用的接收方的類型為 dynamic
,則會在運行時(而不是在編譯時)進行重載決策。 在以下示例中,如果唯一可訪問的 exampleMethod2
方法定義為接受字符串參數,則將 d1
作為參數發送不會導致編譯器錯誤,但卻會導致運行時異常。 重載決策之所以會在運行時失敗,是因為 d1
的運行時類型為 int
,而 exampleMethod2
要求為字符串。
ec.exampleMethod2("a string"); ec.exampleMethod2(d1);
dynamic
類型的基礎結構,還提供了 IronPython 和 IronRuby 等動態編程語言的實現。 有關 DLR 的詳細信息,請參閱動態語言運行時概述。
C# 4 包括若干功能,這些功能改善了與 COM API(例如 Office 自動化 API)的互操作體驗。 這些改進之處包括 dynamic
類型以及命名參數和可選參數的用法。
通過將類型指定為 object
,許多 COM 方法都允許參數類型和返回類型發生變化。 這樣,就必須顯式強制轉換值,以便與 C# 中的強類型變量保持協調。 如果使用 /link(C# 編譯器選項)選項進行編譯,則可以通過引入 dynamic
類型將 COM 簽名中出現的 object
看作是 dynamic
類型,從而避免大量的強制轉換。 例如,以下語句對比了在使用 dynamic
類型和不使用 dynamic
類型的情況下如何訪問 Microsoft Office Excel 電子表格中的單元格。
// 引入動態之前 ((Excel.Range)excelApp.Cells[1, 1]).Value2 = "Name"; Excel.Range range2008 = (Excel.Range)excelApp.Cells[1, 1];
// 在引入 dynamic 之后,對 value 屬性的訪問以及到 excel.range 的轉換將由運行時 COM 綁定器處理 excelApp.Cells[1, 1].Value = "Name"; Excel.Range range2010 = excelApp.Cells[1, 1];
其他技術請參考
Title | 說明 |
---|---|
dynamic | 描述 dynamic 關鍵字的用法。 |
動態語言運行時概述 | 提供有關 DLR 的概述,DLR 是一種運行時環境,它將一組適用於動態語言的服務添加到公共語言運行時 (CLR)。 |
演練:創建和使用動態對象 | 提供有關如何創建自定義動態對象以及創建訪問 IronPython 庫的對象的分步說明。 |
如何:通過使用 Visual C# 功能訪問 Office 互操作對象 | 演示如何創建一個項目,該項目使用命名參數和可選參數、dynamic 類型以及可簡化對 Office API 對象的訪問的其他增強功能。 |