这是
动态语言运行时系列的最后一部分。 以前的文章:
- 动态细节:编译器卧底游戏,内存泄漏,性能差异 。 本文详细讨论了DLR缓存以及对开发人员重要的要点。
- Grokl DLR 。 技术概述,DynamicMetaObject的剖析以及有关如何创建自己的动态类的简短说明。
在这篇简短的文章中,我们最终将分析在现实生活中使用
动态的主要情况:什么时候没有它,什么时候可以使生活更轻松。

当动态必不可少
没有这种情况。 您可以始终以静态样式编写功能相似的代码,唯一的区别是易于阅读和代码量大。 例如,当使用COM对象而不是
动态对象时,可以使用反射。
动态时有用
使用COM对象
首先,当然,这是与COM对象一起工作的,因此所有这些都已开始。 将获得的代码与
动态和反射进行比较:
dynamic instance = Activator.CreateInstance(type); instance.Run("Notepad.exe");
var instance = Activator.CreateInstance(type); type.InvokeMember("Run", BindingFlags.InvokeMethod, null, instance, new[] { "Notepad.exe" });
通常,要通过反射使用COM对象,必须为每个方法/属性创建带有包装的分支类。 还有一些不太明显的优点,例如,通过
dynamic调用方法时,不需要填写不需要的参数(从COM对象的角度来看是强制性的)的能力。
使用配置
另一个教科书示例正在使用
XML等配置。 没有
动态 :
XElement person = XElement.Parse(xml); Console.WriteLine( $"{person.Descendants("FirstName").FirstOrDefault().Value} {person.Descendants("LastName").FirstOrDefault().Value}" );
具有动态:
dynamic person = DynamicXml.Parse(xml); Console.WriteLine( $"{person.FirstName} {person.LastName}" );
当然,为此,您需要实现自己的动态类。 作为第一个清单的替代方法,您可以编写一个类似这样的类:
var person = StaticXml.Parse(xml); Console.WriteLine( $"{person.GetElement("FirstName")} {person.GetElement("LastName")}" );
但是,您看,这看起来比通过
dynamic显得优雅得多。
使用外部资源
上一段可以概括为使用外部资源进行的任何操作。 我们总是有两种选择:使用
动态方式以本机C#样式获取代码,或使用“魔术线”进行静态键入。 让我们看一个带有
REST API请求的示例。 使用dynamic,您可以这样编写:
dynamic dynamicRestApiClient = new DynamicRestApiClient("http://localhost:18457/api"); dynamic catsList = dynamicRestApiClient.CatsList;
我们的动态类将根据属性的请求发送表单请求
[GET] http://localhost:18457/api/catslist
然后,他将其反序列化并返回给我们一系列已经准备好用于预期用途的猫。 没有
动态,它将看起来像这样:
var restApiClient = new RestApiClient("http://localhost:18457/api"); var catsListJson = restApiClient.Get("catsList"); var deserializedCatsList = JsonConvert.DeserializeObject<Cat[]>(catsListJson);
反射更换
在前面的示例中,您可能会遇到一个问题:为什么在一种情况下我们将返回值反序列化为特定类型,而在另一种情况下却没有呢? 事实是,在静态类型化中,我们需要将对象显式转换为
Cat类型才能使用它们。 在
dynamic的情况下,将
JSON反序列化为我们的动态类中的对象数组并从中返回
object []就足够了,因为
dynamic负责反射。 我将给出两个示例说明其工作原理:
dynamic deserialized = JsonConvert.DeserializeObject<object>(serialized); var name = deserialized.Name; var lastName = deserialized.LastName;
Attribute[] attributes = type.GetCustomAttributes(false).OfType<Attribute>(); dynamic attribute = attributes.Single(x => x.GetType().Name == "DescriptionAttribute"); var description = attribute.Description;
与使用COM对象时的原理相同。
参观者
使用
动态,您可以非常优雅地实现此模式。 而不是一千个字:
public static void DoSomeWork(Item item) { InternalDoSomeWork((dynamic) item); } private static void InternalDoSomeWork(Item item) { throw new Exception("Couldn't find handler for " + item.GetType()); } private static void InternalDoSomeWork(Sword item) {
现在,将
Sword类型的对象传递给
DoSomeWork方法时,将调用
InternalDoSomeWork(Sword项目)方法。
结论
使用
动态的优点:
- 可用于快速原型制作:在大多数情况下,样板代码的数量减少
- 通常,它可以提高代码的可读性和美观性(由于从“魔术线”过渡到语言的本机样式)
- 尽管有广泛的意见,但由于有了缓存机制,一般情况下并不会产生显着的性能开销
使用动态的缺点:
- 内存和性能之间存在明显的细微差别。
- 在此类动态类的支持和阅读下,您需要很好地了解正在发生的事情
- 程序员被剥夺了类型检查和编译器提供的所有运行状况保证
结论
我认为,在以下情况下,使用动态开发人员将获得最大的收益:
- 原型制作时
- 在小型/家庭项目中,错误成本低
- 在小的代码大小的实用程序中,并不意味着运行时间长。 如果您的实用程序在最坏的情况下执行了几秒钟,则通常无需考虑内存泄漏和性能下降
至少有争议的是在具有大量代码库的复杂项目中使用
动态 -在这里最好花一些时间编写静态包装,从而将不明显的时刻减少到最少。
如果您使用的COM对象或服务/产品中的域意味着连续的长时间工作,则最好不要使用
dynamic ,尽管它是为此类情况而创建的。 即使您完全知道怎么做和怎么做,也不会犯错,但迟早会有一个不知道这一点的新开发人员来临。 结果很可能是难以计算的内存泄漏。