VS2019中Dictionary<string,dynamic>在函数内外使用的行为差异及取值问题咨询
这个问题的核心在于**JavaScriptSerializer.Deserialize<dynamic>返回的并不是真正的动态对象**,它底层其实是Dictionary<string, object>,而非支持动态成员访问的ExpandoObject类型。这就导致了函数内部和外部的行为差异:
为什么函数内部正常,外部报错?
在LoadStuff函数内部,你直接将反序列化结果赋值给dynamic o,编译器会允许你使用索引器(因为Dictionary<string, object>本身就自带索引器),而且此时编译器能通过上下文推断出这个动态对象的实际类型支持索引操作,所以运行和调试都没问题。
但当你把这个对象存入Dictionary<string, dynamic>并在函数外部取出时,虽然你声明了dynamic o,但编译器在编译阶段无法确定这个对象的具体类型(动态绑定的延迟特性),再加上JavaScriptSerializer返回的Dictionary<string, object>在动态语境下的绑定限制,就会触发编译错误CS0021——编译器误以为你在对object类型使用索引器。
解决方法
方法1:改用Newtonsoft.Json(推荐)
JavaScriptSerializer本身对dynamic的支持比较有限,换成更现代的Newtonsoft.Json(Json.NET)可以彻底解决这个问题。它的JObject类型完美支持动态索引和成员访问:
// 先安装Newtonsoft.Json NuGet包 using Newtonsoft.Json; private void LoadStuff(ref Dictionary<string, dynamic> dictGen) { dynamic o = JsonConvert.DeserializeObject<dynamic>("{}"); o["matchField"] = "i"; o["newField"] = "language"; dictGen.Add("sub div", o); } // 调用代码 Dictionary<string, dynamic> dictGen = new Dictionary<string, dynamic>(); LoadStuff(ref dictGen); dynamic o = dictGen["sub div"]; string value = o["matchField"]; // 正常工作,不会报错
方法2:显式转换为Dictionary<string, object>
如果你必须使用JavaScriptSerializer,可以在取出对象时显式转换为Dictionary<string, object>,再访问索引:
// 调用代码中 dynamic o = dictGen["sub div"]; var dictObj = o as Dictionary<string, object>; if (dictObj != null) { string matchField = dictObj["matchField"] as string; // 或者直接用dynamic访问:string matchField = dictObj["matchField"]; }
方法3:将反序列化结果转为ExpandoObject
你可以写一个辅助方法,把JavaScriptSerializer返回的Dictionary<string, object>转换成ExpandoObject,这样就能像真正的动态对象一样使用:
private static ExpandoObject ToExpandoObject(Dictionary<string, object> dict) { var expando = new ExpandoObject(); var expandoDict = (IDictionary<string, object>)expando; foreach (var kvp in dict) { if (kvp.Value is Dictionary<string, object> nestedDict) { expandoDict[kvp.Key] = ToExpandoObject(nestedDict); } else { expandoDict[kvp.Key] = kvp.Value; } } return expando; } // 修改LoadStuff函数 private void LoadStuff(ref Dictionary<string, dynamic> dictGen) { JavaScriptSerializer serializer = new JavaScriptSerializer(); var dictObj = serializer.Deserialize<Dictionary<string, object>>("{}"); dictObj["matchField"] = "i"; dictObj["newField"] = "language"; dynamic o = ToExpandoObject(dictObj); dictGen.Add("sub div", o); } // 调用代码中,现在可以直接用索引访问 dynamic o = dictGen["sub div"]; string value = o["matchField"]; // 正常工作
内容的提问来源于stack exchange,提问作者Velocedge

