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 = ;
object obj = ; System.Console.WriteLine(dyn.GetType());
System.Console.WriteLine(obj.GetType());
}
}

输出结果:

若要查看编译时 dyn 与 obj 之间的区别,请在前面示例的声明和 WriteLine 语句之间添加下列两行:

dyn = dyn + ;
obj = obj + ;

尝试在表达式 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 = ; if (d is int)
{
return local;
}
else
{
return two;
}
}
}
  • 在显式类型转换中,作为转换的目标类型。

static void convertToDynamic()
{
dynamic d;
int i = ;
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 = ;
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());
Console.WriteLine(ec.exampleMethod("value")); // 下面的语句会引发编译器异常。因为 exampleMethod 方法仅包含一个参数
//Console.WriteLine(ec.exampleMethod(10, 4)); dynamic dynamic_ec = new ExampleClass();
Console.WriteLine(dynamic_ec.exampleMethod()); // 因为 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 = ; if (d is int)
{
return local;
}
else
{
return two;
}
}
}
}
// 输出结果:
// Local variable
// 2
// Local variable
使用 dynamic

  以上示例中,编译器的作用是将有关每个语句的预期作用的信息一起打包到类型化为 dynamic 的对象或表达式。 在运行时,将对存储的信息进行检查,并且任何无效的语句都将导致运行时异常。

大多数动态操作的结果是其本身 dynamic。 例如,如果将鼠标指针放在以下示例中使用的 testSum 上,则 IntelliSense 将显示类型“(局部变量)dynamic testSum” 。

dynamic d = ;
var testSum = d + ; System.Console.WriteLine(testSum);

结果不为 dynamic 的操作包括:

  • 从 dynamic 到另一种类型的转换。
  • 包括类型为 dynamic 的自变量的构造函数调用。

例如,以下声明中 testInstance 的类型为 ExampleClass,而不是 dynamic:

var testInstance = new ExampleClass(d);
转换

动态对象和其他类型之间的转换非常简单。 这样,开发人员将能够在动态行为和非动态行为之间切换。

任何对象都可隐式转换为动态类型,如以下示例所示。

dynamic d1 = ;
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,或者方法调用的接收方的类型为 dynamic,则会在运行时(而不是在编译时)进行重载决策。 在以下示例中,如果唯一可访问的 exampleMethod2 方法定义为接受字符串参数,则将 d1 作为参数发送不会导致编译器错误,但却会导致运行时异常。 重载决策之所以会在运行时失败,是因为 d1 的运行时类型为 int,而 exampleMethod2 要求为字符串。
ec.exampleMethod2("a string");

ec.exampleMethod2(d1);
动态语言运行时
  动态语言运行时 (DLR) 是 .NET Framework 4 中的一个新 API。 它提供了支持 C# 中 dynamic 类型的基础结构,还提供了 IronPython 和 IronRuby 等动态编程语言的实现。 有关 DLR 的详细信息,请参阅动态语言运行时概述。
COM 互操作

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[, ]).Value2 = "Name";
Excel.Range range2008 = (Excel.Range)excelApp.Cells[, ];
// 在引入 dynamic 之后,对 value 属性的访问以及到 excel.range 的转换将由运行时 COM 绑定器处理
excelApp.Cells[, ].Value = "Name";
Excel.Range range2010 = excelApp.Cells[, ];

其他技术请参考

Title 说明
dynamic 描述 dynamic 关键字的用法。
动态语言运行时概述 提供有关 DLR 的概述,DLR 是一种运行时环境,它将一组适用于动态语言的服务添加到公共语言运行时 (CLR)。
演练:创建和使用动态对象 提供有关如何创建自定义动态对象以及创建访问 IronPython 库的对象的分步说明。
如何:通过使用 Visual C# 功能访问 Office 互操作对象 演示如何创建一个项目,该项目使用命名参数和可选参数、dynamic 类型以及可简化对 Office API 对象的访问的其他增强功能。
 

C#4.0新增功能01 动态绑定 (dynamic 类型)的更多相关文章

  1. C#2.0新增功能01 分布类与分部方法

    连载目录    [已更新最新开发文章,点击查看详细] 分部类型 拆分一个类.一个结构.一个接口或一个方法的定义到两个或更多的文件中, 每个源文件包含类型或方法定义的一部分,编译应用程序时将把所有部分组 ...

  2. C#5.0新增功能01 异步编程

    连载目录    [已更新最新开发文章,点击查看详细] 如果需要 I/O 绑定(例如从网络请求数据或访问数据库),则需要利用异步编程. 还可以使用 CPU 绑定代码(例如执行成本高昂的计算),对编写异步 ...

  3. C#3.0新增功能01 自动实现的属性

    连载目录    [已更新最新开发文章,点击查看详细] 在 C# 3.0 及更高版本,当属性访问器中不需要任何其他逻辑时,自动实现的属性会使属性声明更加简洁. 它们还允许客户端代码创建对象. 当你声明以 ...

  4. C#3.0新增功能03 隐式类型本地变量

    连载目录    [已更新最新开发文章,点击查看详细] 从 Visual C# 3.0 开始,在方法范围内声明的变量可以具有隐式“类型”var. 隐式类型本地变量为强类型,就像用户已经自行声明该类型,但 ...

  5. C#基础拾遗系列之二:使用ILSpy探索C#7.0新增功能点

    C#基础拾遗系列之二:使用ILSpy探索C#7.0新增功能点   第一部分: C#是一种通用的,类型安全的,面向对象的编程语言.有如下特点: (1)面向对象:c# 是面向对象的范例的一个丰富实现, 它 ...

  6. C#2.0新增功能06 协变和逆变

    连载目录    [已更新最新开发文章,点击查看详细] 在 C# 中,协变和逆变能够实现数组类型.委托类型和泛型类型参数的隐式引用转换. 协变保留分配兼容性,逆变则与之相反. 以下代码演示分配兼容性.协 ...

  7. 说说C# 8.0 新增功能Index和Range的^0是什么?

    前言 在<C# 8.0 中使用 Index 和 Range>这篇中有人提出^0是什么意思?处于好奇就去试了,结果抛出异常.查看官方文档说^0索引与 sequence[sequence.Le ...

  8. C#基础拾遗系列之二:C#7.0新增功能点

    第一部分: C#是一种通用的,类型安全的,面向对象的编程语言.有如下特点: (1)面向对象:c# 是面向对象的范例的一个丰富实现, 它包括封装.继承和多态性.C#面向对象的行为包括: 统一的类型系统 ...

  9. C#7.0新增功能点

    原文地址:  https://www.cnblogs.com/runningsmallguo/p/8972678.html 第二部分:C#7.0新增的功能 (1)数字字面量的提升: C#7中的数字文字 ...

随机推荐

  1. 演练:创建和使用动态链接库 (C++)

    我们将创建的第一种类型的库是动态链接库 (DLL). 使用 DLL 是一种重用代码的绝佳方式. 您不必在自己创建的每个程序中重新实现同一例程,而只需对这些例程编写一次,然后从需要该功能的应用程序引用它 ...

  2. ASP.NET Web API 直到我膝盖中了一箭【1】基础篇

    蓦然回首,那些年,我竟然一直很二. 小时候,读武侠小说的时候,看到那些猪脚,常常会产生一种代入感,幻想自己也会遭遇某种奇遇,遇到悬崖跳下去是不是有本“武林秘笈”在等着?长大以后也是一样,多少人梦着醒着 ...

  3. pytorch实现yolov3(3) 实现forward

    之前的文章里https://www.cnblogs.com/sdu20112013/p/11099244.html实现了网络的各个layer. 本篇来实现网络的forward的过程. 定义网络 cla ...

  4. VUE单页面的应用优缺点

    1.优 分离前后端关注点,前端负责界面显示,后端负责数据存储和计算. 减轻服务器压力,服务器只用出数据就可以: 同一套后端程序代码,不用修改就可以用于多种设备客户端: 2019-06-19用户体验好. ...

  5. jdk9新特性之jShell

    jdk9还没研究完,结果jdk10都停止维护了. 最近回顾jdk9,发现了一个新特性--jShell. jdk9是在2017年的9月份发布的,这是我开始感觉入门java的时间.从jdk10开始就是半年 ...

  6. while循环语句、格式化输出、常用运算符、字符编码

    1.while循环 while 空格 条件 冒号 缩进 循环体 num=1 while num<11: print(num) num=num+1 变量都是先执行等号右边的,然后执行等号左边的. ...

  7. CSS3 - vue中纯css实现柱状图表效果

    背景 以前我们制作柱状图都用echarts或者其他同类型的图表插件 这次是个移动端的需求,而且这个图表需要动画 使用echarts就会显得过重,而且动画达不到我想要的效果(主要是我自己愚蠢想不到好的动 ...

  8. 数读 | 为什么运维朋友们都需要学Python?

    运维人员需不需要学开发?需不需要学Python?PythonN和Shell有什么区别?天天问这种好水的问题,我实在受不了,决定帮大家扫扫盲. 现阶段,掌握一门开发语言已经成为高级运维工程师的必备计能, ...

  9. MySQL数据库修改字段的长度

    数据库版本:5.7.22 使用DDL语句:alter table 表名 modify 字段名 字符类型(长度) 例如: alter table db2.admin modify password );

  10. 吐槽:那些Java设计中不得不说的槽点

    1. 求长度各有千秋 你是否曾经在面试的时候,经常被问到:数组有没有 length() 方法?字符串有没有 length() 方法? 集合有没有 length() 方法? 面对这个问题,那么不得不吐槽 ...