当前位置: 首页 > news >正文

C# dynamic 关键字 使用详解

总目录


前言

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.String

value = 42;                        // 动态变量可重新赋值为任意类型
Console.WriteLine(value.GetType()); // 输出: System.Int32

value = "Hello";                   // 运行时切换为 string
Console.WriteLine(value.Length);  // 输出 5

value = 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"]);  // 输出 Bob
        Console.WriteLine(dict["Age"]);  // 输出 25
        Action 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);  // 输出 Alice
        Console.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 知识汇总
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。

相关文章:

  • wps中的js开发
  • STM32 看门狗
  • mac相关命令
  • Linux环境Docker使用代理推拉镜像
  • DeepSeek R1 模型在架构上的优势
  • 【基础架构篇九】《DeepSeek模型版本管理:Git+MLflow集成实践》
  • VMware Workstation 17.0 Pro创建虚拟机并安装Ubuntu22.04与ubuntu20.04(双版本同时存在)《包含小问题总结》
  • Servlet中HttpServletRequest和HttpServletResponse的常用API
  • 一文读懂Docker之Dockerfile基本使用
  • 《手机装上DeepSeek R1,开启口袋里的AI超能力》
  • Android Studio:如何用一个store类管理事件、简化管理
  • 详解AbstractQueuedSynchronizer(AQS)源码
  • 考场考试监考系统
  • QT自定义扫描控件,支持任意方位拖动和拖动扫描范围。
  • 数据结构(查找)
  • Python高级语法之urllib
  • Android Studio打包APK
  • 数字化赋能,3D数字博物馆开启沉浸式体验
  • 机器学习(李宏毅)——RNN
  • 第一章 初识LLMs
  • 印度杰纳布河上游两座水电站均已重新开闸
  • 大四本科生已发14篇SCI论文?学校工作人员:已记录汇报
  • 李公明︱一周书记:浪漫主义为什么……仍然重要?
  • 观察|印巴交火开始升级,是否会升级为第四次印巴战争?
  • 【社论】三个“靠谱”为市场注入确定性
  • 解读|降准叠加政策利率、公积金贷款利率、结构性政策工具利率全线下调,影响有多大?