C# dynamic 关键字 使用详解

devtools/2025/2/23 0:37:10/

总目录


前言

dynamic 是 C# 4.0 引入的关键字,用于声明动态类型,允许在运行时解析类型和成员,而非编译时。它主要设计用于简化与动态语言(如 Python、JavaScript)的交互、处理未知结构的数据(如 JSON、XML)以及减少反射代码的复杂性。


一、基本概念

  • 动态类型解析:编译器不会对 dynamic 变量进行类型检查,所有操作(方法调用、属性访问)在运行时解析。
  • 底层机制:由 DLR(Dynamic Language Runtime)驱动,使用 IDynamicMetaObjectProvider 接口
  • 适用场景
    • 与动态语言(IronPython、JavaScript)交互。
    • 处理未知结构的动态数据(如反序列化 JSON)。
    • 简化反射代码。
    • COM 互操作(如操作 Excel 对象模型)。
  • 性能代价:动态类型解析比静态类型慢,需谨慎使用高频代码。

二、基本用法

1. 动态类型申明

dynamic value = "Hello World";
Console.WriteLine(value.GetType()); // 输出: System.Stringvalue = 42;                        // 动态变量可重新赋值为任意类型
Console.WriteLine(value.GetType()); // 输出: System.Int32value = "Hello";                   // 运行时切换为 string
Console.WriteLine(value.Length);  // 输出 5value = new List<int> { 1, 2, 3 };
value.Add(4);              		  // 运行时调用 Add 方法dynamic person = new { Name = "John", Age = 30 };
Console.WriteLine(person.Name); // 输出: John

2. 调用未知方法或属性

dynamic obj = new ExpandoObject();
obj.Name = "Alice";                // 动态添加属性
obj.Print = new Action(() => Console.WriteLine(obj.Name));obj.Print();                       // 输出: Alice

3. 动态方法调用

public class Calculator
{public int Add(int a, int b) => a + b;
}class Program
{static void Main(){dynamic calc = new Calculator();int result = calc.Add(3, 4);       // 编译时不检查方法是否存在Console.WriteLine(result);         // 输出: 7}
}

三、dynamic vs object vs var

特性dynamicobjectvar
类型检查时机运行时编译时编译时(类型推断)
成员访问动态解析需要强制转换静态类型访问
性能较慢需要拆箱最优
使用场景动态交互通用对象容器类型声明简化
智能提示支持
代码灵活性高(适应未知结构)低(需明确类型)适中

四、应用场景

1. 动态类型与反射

动态类型可简化反射操作,但需权衡性能与可读性:

反射实现

object obj = Activator.CreateInstance(typeof(MyClass));
MethodInfo method = typeof(MyClass).GetMethod("MyMethod");
method.Invoke(obj, new object[] { 42 });

动态类型实现

dynamic obj = Activator.CreateInstance(typeof(MyClass));
obj.MyMethod(42);  // 代码更简洁
// 传统反射方式
object obj = Activator.CreateInstance(typeof(MyClass));
MethodInfo method = obj.GetType().GetMethod("DoWork");
method.Invoke(obj, null);// 使用 dynamic 简化
dynamic dynObj = Activator.CreateInstance(typeof(MyClass));
dynObj.DoWork();  // 直接调用方法

2. 动态对象(ExpandoObjectDynamicObject

1. ExpandoObject

允许动态添加属性和方法:

    static void Main(){dynamic person = new ExpandoObject();person.Name = "Bob";person.Age = 25;person.SayHello = (Action)(() => Console.WriteLine($"Hi, I'm {person.Name}"));person.SayHello();  // 输出 "Hi, I'm Bob"// 转换为字典var dict = (IDictionary<string, object>)person;Console.WriteLine(dict["Name"]);  // 输出 BobConsole.WriteLine(dict["Age"]);  // 输出 25Action action= (Action)dict["SayHello"];action?.Invoke();               //输出 "Hi, I'm Bob"}

2. 自定义 DynamicObject

实现动态行为控制:

public class DynamicDictionary : DynamicObject
{private Dictionary<string, object> _dict = new Dictionary<string, object>();public override bool TryGetMember(GetMemberBinder binder, out object result){return _dict.TryGetValue(binder.Name, out result);}public override bool TrySetMember(SetMemberBinder binder, object value){_dict[binder.Name] = value;return true;}
}class Program
{static void Main(){dynamic dict = new DynamicDictionary();dict.City = "Shanghai";Console.WriteLine(dict.City); // 输出: Shanghai}
}

3. 处理动态数据(JSON 示例)

使用 Newtonsoft.Json(或 System.Text.Json)处理动态 JSON:

    static void Main(){string json= """{ "Name": "Alice", "Age": 30 }""";dynamic data = JObject.Parse(json); //或使用下面的方式//dynamic data = JsonConvert.DeserializeObject<dynamic>(json);Console.WriteLine(data.Name);  // 输出 AliceConsole.WriteLine(data.Age + 5); // 输出 35// 动态扩展属性data.Country = "USA";  // 运行时添加新属性Console.WriteLine(data.Country);// 输出 USA}

4. COM 互操作(Excel 自动化)

Type excelType = Type.GetTypeFromProgID("Excel.Application");
dynamic excel = Activator.CreateInstance(excelType);excel.Visible = true;          // 设置属性
dynamic workbook = excel.Workbooks.Add();
dynamic worksheet = workbook.ActiveSheet;worksheet.Cells[1, 1] = "Hello World";  // 动态访问单元格

五、注意事项与最佳实践

1. 注意事项

  • 避免过度使用
    • 优先使用静态类型确保安全性和性能。
    • 仅在必要时(如处理动态数据、COM 互操作)使用 dynamic
  • 无智能提示
    • 成员解析完全在运行时完成
  • 错误处理
    • 动态调用可能抛出 RuntimeBinderException,需捕获异常。
    • 错误延迟:类型错误在运行时才会暴露
    try
    {dynamic obj = new object();obj.InvalidMethod();
    }
    catch (RuntimeBinderException ex)
    {Console.WriteLine($"运行时错误: {ex.Message}");
    }
    

2. 最佳实践

  • 性能优化
    • 缓存频繁使用的动态操作结果。
    • 在循环或高频代码中避免使用 dynamic
// 缓存高频操作(减少动态解析次数)
dynamic obj = GetDynamicObject();
var cachedAction = (Action)obj.DoWork;  // 转换为委托
for(int i=0; i<1000; i++) {cachedAction();  // 比直接调用 obj.DoWork() 更快
}
  • 严格限制使用范围:仅在必需时使用
    • 泛型与接口:通过设计模式避免动态类型。
  • 配合 try-catch:处理可能的运行时异常
  • 单元测试覆盖:针对动态代码增加测试用例
  • 优先选择替代方案
    • 对于已知结构:使用强类型类
    • 对于 JSON:推荐 System.Text.Json 的强类型反序列化
    • 对于反射:考虑 generic 或 delegate 优化

结语

回到目录页:C#/.NET 知识汇总
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。


http://www.ppmy.cn/devtools/161069.html

相关文章

新能源汽车核心元件揭秘:二极管、三极管结构与工作原理解析(2/2)

上一节我们讲了二极管的原理, 原文章: https://zhuanlan.zhihu.com/p/25252117833 看了的朋友应该很容易懂这节课 这篇文章我们来说说三极管的工作原理啊 这里要说下几个概念 1 半导体的导通, 就是说里面的负电荷电子和正电荷空穴可以大量的从 一个地方达到我们想要的地方…

通义灵码AI程序员

通义灵码是阿里云与通义实验室联合打造的智能编码辅助工具&#xff0c;基于通义大模型技术&#xff0c;为开发者提供多种编程辅助功能。它支持多种编程语言&#xff0c;包括 Java、Python、Go、TypeScript、JavaScript、C/C、PHP、C#、Ruby 等 200 多种编码语言。 通义灵码 AI…

系统学习算法:专题十一 floodfill算法

介绍&#xff1a; floodfill算法简单来说就是求出相同性质的联通块 比如在上面这个矩阵中&#xff0c;如果我们要求出所有负数的联通块&#xff0c;就可以使用floodfill算法&#xff0c;但联通必须是上下左右&#xff0c;斜对角的不行 其中实现的方法有深度优先遍历&#xff…

1.21作业

1 unserialize3 当序列化字符串中属性个数大于实际属性个数时&#xff0c;不会执行反序列化 外部如果是unserialize&#xff08;&#xff09;会调用wakeup&#xff08;&#xff09;方法&#xff0c;输出“bad request”——构造url绕过wakeup 类型&#xff1a;public class&…

A. C05.L08.贪心算法入门

这套题包含了历年真题&#xff0c;十分重要&#xff01;&#xff01;&#xff01;&#xff01;要考试的同学可以参考一下&#xff01;&#xff01; 此套题限时3小时。 A. C05.L08.贪心算法入门&#xff08;一&#xff09;.课堂练习1.书架(SSOI2017五年级t6) 传统题1000ms256…

25会计研究生复试面试问题汇总 会计专业知识问题很全! 会计复试全流程攻略 会计考研复试真题汇总

宝子们&#xff0c;会计考研复试快到了&#xff0c;是不是有点慌&#xff1f;别怕&#xff01;今天学姐给你们支招&#xff0c;手把手教你搞定复试面试&#xff0c;直接冲上岸&#xff01;快来看看怎么准备吧&#xff0c;时间紧直接背第三部分的面试题&#xff01; 目录 一、复…

如何在 SpringBoot 项目使用 Redis 的 Pipeline 功能

本文是博主在批量存储聊天中用户状态和登陆信息到 Redis 缓存中时&#xff0c;使用到了 Pipeline 功能&#xff0c;并对此做出了整理。 一、Redis Pipeline 是什么 Redis 的 Pipeline 功能可以显著提升 Redis 操作的性能&#xff0c;性能提升的原因在于可以批量执行命令。当我…

微信小程序——访问服务器媒体文件的实现步骤

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;趣享先生的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&…