总目录
前言
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
特性 | dynamic | object | var |
---|---|---|---|
类型检查时机 | 运行时 | 编译时 | 编译时(类型推断) |
成员访问 | 动态解析 | 需要强制转换 | 静态类型访问 |
性能 | 较慢 | 需要拆箱 | 最优 |
使用场景 | 动态交互 | 通用对象容器 | 类型声明简化 |
智能提示支持 | 无 | 有 | 有 |
代码灵活性 | 高(适应未知结构) | 低(需明确类型) | 适中 |
四、应用场景
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. 动态对象(ExpandoObject
和 DynamicObject
)
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 知识汇总
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。