动态朗。运行时与反射
Posted
技术标签:
【中文标题】动态朗。运行时与反射【英文标题】:Dynamic Lang. Runtime vs Reflection 【发布时间】:2011-06-06 12:23:36 【问题描述】:我打算为我的新项目使用动态关键字。但在介入之前,我想了解使用动态关键字而不是反射的利弊。
根据优点,我可以找到关于动态关键字的信息:
可读\可维护的代码。 代码行更少。虽然与使用动态关键字相关的否定词,但我听说是这样的:
影响应用程序性能。 Dynamic 关键字在内部是反射的包装器。 动态类型可能会成为难以发现的错误的温床。 影响与先前 .NET 版本的互操作性。请帮我看看我遇到的利弊是否明智?
【问题讨论】:
你需要做private反射吗? 不,我不需要私人反思。我需要调用的任何方法总是公开的。 【参考方案1】:请帮我看看我遇到的利弊是否明智?
我对您的优缺点的担忧是,其中一些没有解决使用反射和使用动态之间的差异。这种动态类型会导致直到运行时才被捕获的错误对于任何动态类型系统都是正确的。反射代码与使用动态类型的代码一样可能存在错误。
与其从利弊的角度来考虑,不如从更中性的角度来考虑。我要问的问题是“使用 Reflection 和使用动态类型之间的区别是什么?”
首先:通过反射,您可以完全获得您所要求的。使用动态,您会得到如果在编译时给定类型信息,C# 编译器会做什么。这些可能是两个完全不同的东西。如果您有一个特定方法的 MethodInfo,并且您使用特定参数调用该方法,那么 这就是被调用的方法,句号。如果您使用“动态”,那么您是在要求 DLR 在运行时确定 C# 编译器的意见是什么是正确的调用方法。 C# 编译器可能会选择一种与您实际想要的方法不同的方法。
第二:通过反射,您可以(如果您的代码被授予适当的高度信任)进行私有反射。您可以调用私有方法、读取私有字段等。这样做是否是个好主意,我不知道。对我来说这当然看起来很危险和愚蠢,但我不知道你的应用程序是什么。使用动态,您可以获得从 C# 编译器获得的行为;私有方法和字段不可见。
第三:使用反射,您编写的代码看起来像一个机制。看起来您正在加载元数据源,提取一些类型,提取一些方法信息,并通过方法信息在接收器对象上调用方法。每一步看起来都像是一个机制的运作。有了动态,每一步看起来都像业务逻辑。您在接收器上调用方法的方式与在任何其他代码中执行此操作的方式相同。什么是重要的?在某些代码中,机制实际上是最重要的。在某些代码中,机制实现的业务逻辑是最重要的。选择强调正确抽象级别的技术。
第四:性能成本不同。使用反射,您不会获得任何缓存行为,这意味着操作通常较慢,但维护缓存没有内存成本,并且每个操作的成本大致相同。使用 DLR,第一个操作确实非常慢,因为它会进行大量分析,但分析会被缓存并重用。在某些情况下,这会消耗内存,以换取后续调用的速度提高。对于你的应用程序来说,速度和内存使用的正确平衡是什么,我不知道。
【讨论】:
感谢您对我的查询的详细分析。很高兴知道 DLR 会缓存分析结果。 这是迄今为止这个问题的最佳答案【参考方案2】:可读\可维护的代码
根据我的经验当然是这样。
代码行数更少。
不显着,但会有所帮助。
影响应用程序性能。
非常轻微。但甚至不如反射的方式。
Dynamic 关键字在内部是反射的包装器。
完全不真实。 dynamic 关键字利用动态库运行时。
[编辑:根据下面的评论进行更正]
动态语言运行时似乎确实使用了反射,而性能改进只是由于缓存技术。
动态类型可能会成为难以找到错误的温床。
这可能是真的;这取决于您如何编写代码。您正在有效地从代码中删除编译器检查。如果您的测试覆盖率很好,这可能无关紧要;如果没有,那么我怀疑你会遇到问题。
影响与以前的 .NET 版本的互操作性
不正确。我的意思是您将无法针对旧版本编译代码,但如果您想这样做,那么您应该使用旧版本作为基础并向上编译它,而不是相反。但是如果你想使用 .NET 2 库,那么你应该不会遇到太多问题,只要你在 app.config / web.config 中包含声明即可。
您缺少的一个重要优点是改进了与 COM/ATL 组件的互操作性。
【讨论】:
所以我可以相信你的话,使用动态(DLR)而不是反射不会有任何明显的性能差异。感谢您指出 COM\ATL 上改进的互操作性。 @AbrahamJP - 使用动态比使用反射更快,特别是在重复调用方法时(因为缓存)。 关于“完全不真实。dynamic 关键字利用了动态运行时库。”您认为 DLR 究竟是如何做到的?它有什么魔力可以在无需反射的情况下进行运行时类型分析? DLR 没有任何魔力。当它需要喜欢任何其他代码时,它会使用反射。缓存结果非常聪明,但相信我,它使用反射。 @Eric Lippert - 我的立场是正确的。我知道这比那要复杂一些 比这复杂的很多!并非所有 DLR 分析提供程序都使用反射,但 C# 肯定广泛使用反射来执行运行时类型分析。即使结果被缓存,作为缓存键的运行时类型总是通过反射确定。【参考方案3】:动态和反射之间有 4 个很大的区别。以下是相同的详细解释。参考http://www.codeproject.com/Articles/593881/What-is-the-difference-between-Reflection-and-Dyna
第 1 点。检查 VS 调用
反射可以做两件事,一是它可以检查元数据,二是它还具有在运行时调用方法的能力。而在动态中,我们只能调用方法。因此,如果我正在创建诸如 Visual Studio IDE 之类的软件,那么反射就是要走的路。如果我只想从我的 c# 代码中动态调用,动态是最好的选择。
第 2 点。私有与公开调用
您不能使用动态调用私有方法。反思它可以调用私有方法。
第 3 点。缓存
Dynamic 在内部使用反射,它还增加了缓存优势。因此,如果您只想动态调用一个对象,那么 Dynamic 是最好的,因为您可以获得性能优势。
第 4 点。静态类
动态是特定于实例的:您无权访问静态成员;你必须在这些场景中使用反射。
【讨论】:
【参考方案4】:在大多数情况下,使用 dynamic 关键字不会产生有意义的更短代码。在某些情况下,它会;这取决于提供者,因此这是一个重要的区别。您可能永远不应该使用 dynamic 关键字来访问普通的 CLR 对象;那里的好处太小了。
dynamic 关键字破坏了自动重构工具并使高覆盖率单元测试变得更加重要;毕竟,当你使用它时,编译器并没有检查很多东西。当您与一个非常稳定或固有动态类型的 API 进行互操作时,这并不是什么大问题,但如果您使用关键字 dynamic 访问其 API 可能在未来发生变化的库(例如您自己编写的任何代码),这尤其令人讨厌)。
在有意义的地方谨慎使用关键字,并确保此类代码具有充足的单元测试。不要在不需要或类型推断(例如var
)可以做同样事情的地方使用它。
编辑:您在下面提到您正在为插件执行此操作。 Managed Extensibility Framework 的设计考虑了这一点 - 关键字 dynamic
和反射可能是更好的选择。
【讨论】:
@Eamon:如前所述,我没有使用动态关键字来访问普通的 CLR 对象,而是我提到的应用程序提供了插件模型来处理新功能,因为使用了这种反射广泛地。为了使这个模块有效且可维护,我对是使用反射还是动态关键字感到困惑。我最关心的是性能。 @AbrahamJP 您的插件模型是通过接口支持还是通过约定支持?如果使用接口支持,那么无论如何都需要使用很少的反射。 插件不需要动态输入。是否存在性能问题真的取决于您在做什么(数百万次小调用与一两个大调用),但我事先预计性能不会成为问题。跨度> 你考虑过Managed Extensibility Framework吗? @Eamon:对于插件模型,我们使用接口,而XML文件用于处理一些异常情况。调用 XML 文件中定义的方法是复杂的部分。我们动态创建委托来构建函数容器。【参考方案5】:如果您专门使用动态来进行反射,您唯一关心的是与以前版本的兼容性。否则它会胜过反射,因为它更具可读性和更短。无论如何,你会因为使用反射而失去强大的类型和(一些)性能。
【讨论】:
【参考方案6】:在我看来,除了与旧 .NET 版本的互操作性之外,使用反射的所有缺点都存在:
影响应用程序性能
虽然它确实会影响性能,但使用反射也是如此。根据我的记忆,DLR 或多或少在您第一次访问给定类型的动态对象的方法/属性时使用反射并缓存类型/访问目标对,以便以后访问只是在缓存中查找使其更快然后是反射
动态关键字在内部是反射的包装器
即使这是真的(见上文),那怎么会是负面的呢?它是否包含反射不应该在任何重大问题上影响您的应用程序。
动态类型可能会成为难以找到错误的温床
虽然这是真的,但只要您谨慎使用它,应该不会有那么大的问题。此外,您是否基本上将其用作反射的替代品(也就是说,当您想通过反射访问某些内容时,您仅在尽可能短的时间内使用动态),如果您使用反射,此类错误的风险应该不会明显更高访问您的方法/属性(当然,如果您使所有内容都动态化,则问题可能更大)。
影响与以前的 .NET 版本的互操作性
为此,您必须自己决定它对您的关注程度。
【讨论】:
以上是关于动态朗。运行时与反射的主要内容,如果未能解决你的问题,请参考以下文章