理解C# 4 dynamic(3) – DynamicObject的使用

上篇文章"理解C# 4 dynamic(2) – ExpandoObject的使用" 了解了ExpandoObject的基本使用。

但ExpandoObject的问题就是它是一个万金油,什么都可以做,但是又都不专注。

使用DynamicObject正好可以解决这个问题。这篇文章介绍DynamicJson是如何继承DynamicObject,包装一个用来处理Json的动态类型。

 

阅读目录:

一、JS可以灵活处理Json字符串

二、DynamicObject分析

三、DynamicJson代码

四、总结

一,JS可以灵活处理Json字符串

看下面的代码,

var jsonString='{"foo":"json", "bar":100, "nest":{ "foobar":true }}';
var jsonObj = JSON.parse(jsonString);
//接着就能方便的使用
jsonObj.foo
jsonObj.nest.foobar 

代码中的jsonObj是动态类型,在.net中,我们也可以反序列化Json,但都需要指定反序列化后的对象类型。

比如使用Json.Net

Newtonsoft.Json.JsonConvert.DeserializeObject(json, typeof(employee))

有没有办法和js一样,反序列化一个dynamic类型来方便的访问Json数据?

下面先来看看DynamicObject如何使用,然后使用DynamicObject来实现我们的想法。

 

二,DynamicObject分析

DynamicObject有个构造函数,但是protected, 也就是我们没有办法直接实例化来使用它。只能是通过继承来构造DynamicObject的对象。

 

同时DynamicObject中很很多标记为Virtual的方法,比如:

public virtual bool TryGetMember(GetMemberBinder binder, out object result);

public virtual bool TrySetMember(SetMemberBinder binder, object value);

当我们写个类继承DynamicObject, 这个动态类型类的对象,所具有的特性,就是通过重写这些virtual方法体现出来的。

 

假设SampleObject 是继承DynamicObject的类,那么

如果我们重写了TryGetMember, 在调用 int number = sampleObject.Number.时,就会调用TryGetMemeber方法来得到返回值。

如果我们重写了TrySetMember,在调用sampleObject.Number = number 时使用,就会调用TrySetMember方法。

 

了解了DynamicObject, 我们的路线就更加清晰了:

我们要写一个类DynamicJson,继承自DynamicObject

DynamicJson有静态Parse方法,接受一个Json的字符串,返回DynamicJson的对象。

DynamicJson重写TryGetMember方法,这样当访问属性的时候,我们处理,返回正确的值。

三,DynamicJson代码

DynamicJson正是这个思路实现的,下面来分析一下DynamicJson中一些关键代码

Parse静态方法

这段代码非常容易理解,这里将json以xml方式处理,属性变成了xml中的element处理

public static dynamic Parse(string json)
{
    using (var reader = JsonReaderWriterFactory.CreateJsonReader(Encoding.Unicode.GetBytes(json), XmlDictionaryReaderQuotas.Max))
    {
        return ToValue(XElement.Load(reader));
    }
}

ToValue方法

To value方法根据json反序列化后的xml信息,获取element的type属性,然后根据属性分别处理。

如果是非数组和object类型,就直接返回。

如果是,就返回DynamicJson对象,这样当我们使用对象的属性,就会调用TryGetMember方法

private static dynamic ToValue(XElement element)
{
     var type = (JsonType)Enum.Parse(typeof(JsonType), element.Attribute("type").Value);
     switch (type)
     {
         case JsonType.boolean:
             return (bool)element;
         case JsonType.number:
             return (double)element;
         case JsonType.@string:
             return (string)element;
         case JsonType.@object:
         case JsonType.array:
             return new DynamicJson(element, type);
         case JsonType.@null:
         default:
             return null;
    }
}

TryGetMember方法

下面是重写的TryGetMember方法

public override bool TryGetMember(GetMemberBinder binder, out object result)
{

    //根据访问的属性,在序列化的xml结构中寻找子element.
    var element = xml.Element(binder.Name);

    if (element == null)
    {
         result = null;
         return false;
    }

     result = ToValue(element);//如果存在该element, 就继续调用ToValue, 如果是普通类型,就能够返回具体的内容,如果是数组或object,就在返回一个DynamicJson对象。
     return true;
}

四,总结

DynamicJson的代码实现的功能很多,详细的DynamicJson的代码,可以通过Nuget获取。

通过继承DynamicObject类,实现了非常多的功能类,

有针对Json的DynamicJson,还有针对xml的DynamicXml

使用它们,减少了不必要的类型定义,增加了程序代码的灵活性。

转载于:https://www.cnblogs.com/JustRun1983/p/3258540.html

<think>好的,用户现在想了解C#中的dynamic关键字的使用详解。之前他们询问了线程安全集合,现在转向另一个高级特性,dynamic。我需要先回忆一下dynamic的基本概念和应用场景,以及常见的使用案例和注意事项。 首先,dynamic是在C# 4.0引入的,允许绕过编译时类型检查,在运行时解析类型。这为用户提供了灵活性,但也会带来潜在的性能开销和运行时错误的风险。用户可能已经熟悉静态类型,但可能在某些情况下需要动态处理,比如与COM对象交互,处理JSON数据,或者使用反射时简化代码。 接下来,我需要考虑用户可能的背景。他们可能是有一定C#基础的开发者,可能在处理动态数据或与外部系统交互时遇到了问题,所以想了解dynamic如何帮助他们。同时,他们可能想知道使用dynamic的优缺点,以及最佳实践,避免常见的陷阱。 用户的需求是“使用详解”,所以需要详细说明dynamic的用法,包括基本使用、与object的区别、适用场景、注意事项,以及代码示例。同时,可能需要对比静态类型和动态类型,说明何时使用dynamic更合适。 需要确保回答结构清晰,分点讲解,结合示例。例如,动态类型声明、方法调用、属性访问,与ExpandoObject和DynamicObject的结合使用。同时,要提到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&#39;m {person.Name}")); person.SayHello(); // 输出 "Hi, I&#39;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、付费专栏及课程。

余额充值