理解C# 4 dynamic(2) – ExpandoObject的使用

本文探讨了C#中ExpandoObject的应用场景,特别是在不希望创建额外类的情况下,使用ExpandoObject作为轻量级数据容器的优势。同时,也提到了使用ExpandoObject时需要注意的问题,包括其使用的局限性和潜在的维护难度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ExpandoObject的使用非常简单,很容易入手。上一篇里面已经有详细的介绍了,可以看这里
(理解C# 4 dynamic(1) - var, object, dynamic的区别以及dynamic的使用)

下面对ExpandoObject的使用场合和一些认为需要注意的地方,谈一下自己的看法:

一,ExpandoObject使用场合

在传递对象,但是又不想创建一个class或者struct的时候,ExpandoObject就是一个非常好的选择。
假如我们有一个SendMail的函数,功能是发送一个通知邮件给客户,邮件的文本模板,如下:
Dear [Name],
We have sent the gift to your address: [Address]

在具体发送邮件的时候, [Name]和[Address]的内容是从函数GetMailParameters()动态读取出来的。
那么函数GetMailParameters的返回值如何定义好呢?

如果为GetMailParameters()函数创建一个struct或者class来传递, 有些小题大做了,而且定义的struct和class重用性会非常低。

如果使用了ExpandoObject, 就非常容易的解决了这个问题。

复制代码
public dynamic GetMailParameters()
{
     var mailParameters = new ExpandoObject();
     mailParameters.Name=”Peter”;
     mailParameters.Address=”Shanghai China”
     return mail;
}
复制代码

就算以后邮件模板改变,添加了新的变量,也只是简单的在ExpandoObject上扩展一个属性就可以了。

二, 注意问题

由于ExpandObject的先天不足(无特征性):

1. 没有区分的类型名称
2. 没有确切类型定义

如果你看到一个string的函数返回值, 你会想到什么, 你会知道它是一个字符串(这不是废话吗?), 有Length等属性,有IndexOf等方法。
当返回值是MailHelper类对象时,你想只需要直接F12就能看到源码,里面有关于这个类的详细定义,接着你就知道如何使用这个对象了。

当你从一个函数中得到一个ExpandObject的dynamic的返回值时,你知道是什么吗? ExpandObject就是一个黑箱,里面装着什么,谁都不知道。
所以,

1. ExpandObject不能用于太复杂的对象。

ExpandObject最好还是作为简单的数据容器,不要弄得过于复杂,甚至包含有函数处理。

2.ExpandObject的使用范围必须要短

范围短的意思是,产生和使用ExpandObject的代码的路径必须要短(主要是函数调用路径)。如果你正在使用一个ExpandObject对象,查看产生这个ExpandObject的地方,发现分散在好几个函数之中,还有嵌套的话,那么这个ExpandObject是非常难于维护的。

3. ExpandObject的使用场合最好贴近程序的终端。

比如在MVC中的ViewBag, 就是一个好的例子。ViawBag用于生成页面, 而页面就是MVC程序的终端了。到了终端,ExpandObject也就不能祸害它人了。
正是由于ExpandObject的无特征性,什么都可以做,所以容易导致滥用。

下篇介绍如何通过继承DynamicObject和实现IDynamicMetaObjectProvider,为动态类型添加特征性。

 

转载于:https://www.cnblogs.com/webenh/p/7691677.html

<think>好的,用户现在想了解C#中的dynamic关键字的使用详解。之前他们询问了线程安全集合,现在转向另一个高级特性,dynamic。我需要先回忆一下dynamic的基本概念和应用场景,以及常见的使用案例和注意事项。 首先,dynamic是在C# 4.0引入的,允许绕过编译时类型检查,在运行时解析类型。这为用户提供了灵活性,但也会带来潜在的性能开销和运行时错误的风险。用户可能已经熟悉静态类型,但可能在某些情况下需要动态处理,比如与COM对象交互,处理JSON数据,或者使用反射时简化代码。 接下来,我需要考虑用户可能的背景。他们可能是有一定C#基础的开发者,可能在处理动态数据或与外部系统交互时遇到了问题,所以想了解dynamic如何帮助他们。同时,他们可能想知道使用dynamic的优缺点,以及最佳实践,避免常见的陷阱。 用户的需求是“使用详解”,所以需要详细说明dynamic的用法,包括基本使用、与object的区别、适用场景、注意事项,以及代码示例。同时,可能需要对比静态类型和动态类型,说明何时使用dynamic更合适。 需要确保回答结构清晰,分点讲解,结合示例。例如,动态类型声明、方法调用、属性访问,与ExpandoObjectDynamicObject的结合使用。同时,要提到DLR(动态语言运行时)的作用,因为这是dynamic背后的机制。 还要注意用户可能的误区,比如过度使用dynamic导致代码难以维护,或者性能问题。需要强调在必要时才使用,比如处理动态数据或简化反射代码,而不是替代常规的类型安全编程。 可能用户没有提到的深层需求是希望了解dynamic在实际项目中的应用案例,比如处理JSON时替代反序列化,或者与Python等动态语言交互。这时候可以举例如使用Newtonsoft.Json的JObject,或者IronPython的集成。 最后,总结部分要强调dynamic的利弊,帮助用户做出明智的选择。确保回答内容准确,示例正确,避免误导。同时,检查是否有遗漏的重要点,比如与var的区别,或者动态代码的调试挑战。以下是</think>关于 C# `dynamic` 关键字的详细讲解,结合代码示例和场景分析: -- ### 一、`dynamic` 的作用与本质 - **动态类型**:绕过编译时类型检查,在运行时解析成员(方法、属性等) - **底层机制**:由 DLR(Dynamic Language Runtime)驱动,使用 `IDynamicMetaObjectProvider` 接口 - **核心场景**: 1. 处理未知结构的对象(如 JSON、XML) 2. 与动态语言(IronPython、IronRuby)交互 3. 简化反射代码 4. COM 互操作(如 Office 自动化) --- ### 二、基础用法示例 #### 示例 1:动态类型声明 ```csharp dynamic value = 10; // 初始为 int Console.WriteLine(value); // 输出 10 value = "Hello"; // 运行时切换为 string Console.WriteLine(value.Length); // 输出 5 value = new List<int> { 1, 2, 3 }; value.Add(4); // 运行时调用 Add 方法 ``` #### 示例 2:与静态类型对比 ```csharp // 静态类型(编译时检查) string str = "test"; int len = str.Length; // 正确 // 动态类型(运行时检查) dynamic dyn = "test"; int len1 = dyn.Length; // 正确 int len2 = dyn.NonExistentProp; // 运行时抛出 RuntimeBinderException ``` --- ### 三、关键应用场景 #### 场景 1:处理动态数据结构(如 JSON) ```csharp using Newtonsoft.Json.Linq; string json = @"{ ""name"": ""Alice"", ""age"": 30 }"; dynamic data = JObject.Parse(json); Console.WriteLine(data.name); // 输出 Alice Console.WriteLine(data.age + 5); // 输出 35 // 动态扩展属性 data.Country = "USA"; // 运行时添加新属性 ``` #### 场景 2:简化反射代码 ```csharp // 传统反射方式 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(); // 直接调用方法 ``` #### 场景 3:COM 互操作(Excel 自动化) ```csharp 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. 自定义动态对象(`ExpandoObject`) ```csharp 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["Age"]); // 输出 25 ``` #### 2. 实现 `DynamicObject` ```csharp class DynamicDictionary : DynamicObject { private Dictionary<string, object> _dict = new(); public override bool TrySetMember(SetMemberBinder binder, object value) { _dict[binder.Name] = value; return true; } public override bool TryGetMember(GetMemberBinder binder, out object result) { return _dict.TryGetValue(binder.Name, out result); } } // 使用 dynamic myDict = new DynamicDictionary(); myDict.Value = 100; Console.WriteLine(myDict.Value); // 输出 100 ``` --- ### 五、注意事项与性能分析 #### 注意事项: 1. **无智能提示**:成员解析完全在运行时完成 2. **错误延迟**:类型错误在运行时才会暴露 3. **性能开销**:动态解析比静态调用慢约 10-100 倍(需权衡) 4. **不可滥用**:优先使用强类型设计 #### 性能优化技巧: ```csharp // 缓存高频操作(减少动态解析次数) dynamic obj = GetDynamicObject(); var cachedAction = (Action)obj.DoWork; // 转换为委托 for(int i=0; i<1000; i++) { cachedAction(); // 比直接调用 obj.DoWork() 更快 } ``` --- ### 六、`dynamic` vs `object` vs `var` | 特性 | `dynamic` | `object` | `var` | |-----|-------------------| | 类型检查时机 | 运行时 | 编译时 | 编译时(类型推断)| | 成员访问 | 动态解析 | 需要强制转换 | 静态类型访问 | | 性能 | 较慢 | 需要拆箱 | 最优 | | 使用场景 | 动态交互 | 通用对象容器 | 类型声明简化 | --- ### 七、最佳实践 1. **严格限制使用范围**:仅在必需时使用 2. **配合 try-catch**:处理可能的运行时异常 3. **单元测试覆盖**:针对动态代码增加测试用例 4. **优先选择替代方案**: - 对于已知结构:使用强类型类 - 对于 JSON:推荐 `System.Text.Json` 的强类型反序列化 - 对于反射:考虑 `generic` 或 `delegate` 优化 --- 通过合理使用 `dynamic` 关键字,可以在保持代码灵活性的同时处理复杂动态场景,但需要始终警惕其带来的维护成本和性能影响。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值