场景
有个需求,需要传动态参数,想到了匿名类。但在实际操作中,dynamic类型无法获取属性值。
// Assembly_A
public class Client
{
public void OnMessage(dynamic msg)
{
Console.WriteLine(msg.Date);
}
}
// Assembly_B
public class Server
{
public void SendMessage(dynamic msg)
{
var c = new Client();
c.OnMessage(new {Date = "20200828"})
}
}
当运行至Console.WriteLine(msg.Date);
时,抛出异常。
错误信息为:
“object”未包含Date的定义。
“Unhandled Exception: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: ‘object’ does not contain a definition for ‘Value’”
查了资料,原因是匿名类的属性访问权限是internal,只有在同一程序集(Assembly)中才能使用。一旦跨程序集使用,则会出现无法访问的错误。所以dynamic在跨程序集的使用场景中,受到很多的限制。(匿名方法可能也有这样的情况,未证实)。
但我发现,这里有个有意思的情况。虽然跨程序集无法直接调用属性,但是dynamic的数据确实传递过来了。如下图
这样就可以通过json的序列化及反序列化,重新生成一个dynamic类型。
解决方案
using Newtonsoft.Json;
// Assembly_A
public class Client
{
public void OnMessage(dynamic msg)
{
// 收到dynamic的msg后,先序列化成json字符串,然后在将json字符串反序列化成dynamic类型。
var json = JsonConvert.SerializeObject(msg, Formatting.Indented);
var newMsg = JsonConvert.DeserializeObject<dynamic>(json);
Console.WriteLine(newMsg.Date);
}
}
// Assembly_B
public class Server
{
public void SendMessage(dynamic msg)
{
var c = new Client();
c.OnMessage(new {Date = "20200828"})
}
}
需要注意的是,按这种方式反序列化得到的新的,所以数据类型丢失了,变成了object
类型。需要类型转换。
其实,像这种传递变参的场景,使用Dictionary<string, Object>
也可以满足。
按《C#比较dynamic和Dictionary性能》这篇文章的性能测试,dynamic有基础消耗,次数越大,对比Dictionary
形式性能更加占优。但考虑到我这里的场景是跨程序集,dynamic
的使用没有那么流畅,且还多了json
的序列化和反序列化的损耗,感觉得不偿失,最终还是用了Dictionary
的方案。但话说回来,如果不跨程序集,那肯定用dynamic
。
《C# 4.0跨程序集使用"dynamic"》 https://www.cnblogs.com/Gildor/archive/2010/03/19/1689466.html
https://www.cnblogs.com/willingtolove/p/11204018.html
https://www.jianshu.com/p/1d74d6c86295