我想知道 C# 代码中反射的适当性。例如,我编写了一个函数,它遍历给定源对象的属性并创建指定类型的新实例,然后将具有相同名称的属性值从一个复制到另一个。我创建它是为了将数据从一个自动生成的 LINQ 对象复制到另一个对象,以解决 LINQ 中多个表缺乏继承的问题。
然而,我不禁认为这样的代码真的是“作弊”,也就是说,与其使用提供的语言结构来实现给定的目的,不如说它允许您规避它们。
这种代码在多大程度上可以接受?有哪些风险?这种方法的合法用途是什么?
我想知道 C# 代码中反射的适当性。例如,我编写了一个函数,它遍历给定源对象的属性并创建指定类型的新实例,然后将具有相同名称的属性值从一个复制到另一个。我创建它是为了将数据从一个自动生成的 LINQ 对象复制到另一个对象,以解决 LINQ 中多个表缺乏继承的问题。
然而,我不禁认为这样的代码真的是“作弊”,也就是说,与其使用提供的语言结构来实现给定的目的,不如说它允许您规避它们。
这种代码在多大程度上可以接受?有哪些风险?这种方法的合法用途是什么?
有时使用反射可能有点小技巧,但很多时候它只是最棒的代码工具。
查看 .Net 属性网格 - 任何使用过 Visual Studio 的人都会熟悉它。您可以将它指向任何对象,它将生成一个简单的属性编辑器。这使用了反射,事实上大多数 VS 的工具箱都使用了反射。
查看单元测试——它们是通过反射加载的(至少在 NUnit 和 MSTest 中)。
反射允许来自静态语言的动态样式行为。
它真正需要的一件事是鸭子类型——C# 编译器已经支持这一点:你可以做foreach
任何看起来像的东西IEnumerable
,不管它是否实现了接口。您可以在任何具有名为Add
.
在您需要动态样式行为的任何地方使用反射 - 例如,您有一个对象集合并且您想检查每个对象的相同属性。
动态类型的风险是相似的——编译时异常变成运行时异常。您的代码并不“安全”,您必须做出相应的反应。
.Net 反射代码非常快,但没有显式调用那么快。
我同意,它给了我它的工作原理,但感觉就像是黑客的 感觉。我尽量避免反思。在重构包含反射的代码后,我已经被烧毁了很多次。代码编译得很好,测试甚至可以运行,但是在特殊情况下(测试没有涵盖),由于我在反射代码插入的对象之一中进行了重构,程序会破坏运行时。
示例 1: OR 映射器中的反射,您更改对象模型中属性的名称或类型:炸毁运行时。
示例 2:您在 SOA 商店。Web 服务是完全解耦的(或者你是这么认为的)。他们有自己的一组生成的代理类,但是在映射中你决定节省一些时间,你这样做:
ExternalColor c = (ExternalColor)Enum.Parse(typeof(ExternalColor),
internalColor.ToString());
在幕后,这也是反射,但由 .net 框架本身完成。现在,如果您决定将 InternalColor.Grey 重命名为InternalColor.Gray会发生什么?一切看起来都不错,构建良好,甚至运行良好......直到有一天一些愚蠢的用户决定使用灰色......此时映射器会爆炸。
反射是一个很棒的工具,我离不开它。它可以使编程变得更容易和更快。
例如,我在我的 ORM 层中使用反射来为属性分配表中的列值。如果不是为了反射,我必须为每个表/类映射创建一个复制类。
至于上面的外部颜色例外。问题不是 Enum.Parse,而是编码器没有捕捉到正确的异常。由于解析了字符串,因此编码人员应始终假定该字符串可能包含不正确的值。
同样的问题适用于 .Net 中的所有高级编程。“拥有权利的同时也被赋予了重大的责任”。使用反射会给你很大的力量。但请确保您知道如何正确使用它。网上有几十个例子。
It may be just me, but the way I'd get into this is by creating a code generator - using reflection at runtime is a bit costly and untyped. Creating classes that would get generated according to your latest code and copy everything in a strongly typed manner would mean that you will catch these errors at build-time.
For instance, a generated class may look like this:
static class AtoBCopier
{
public static B Copy(A item)
{
return new B() { Prop1 = item.Prop1, Prop2 = item.Prop2 };
}
}
If either class doesn't have the properties or their types change, the code doesn't compile. Plus, there's a huge improvement in times.
我最近在 C# 中使用反射来查找特定接口的实现。我编写了一个简单的批处理式解释器,它根据类名查找每个计算步骤的“动作”。反映当前命名空间然后弹出可以被 Execute()ed 的 IStep 接口的正确实现。这样,添加新的“动作”就像创建新的派生类一样简单——无需将其添加到注册表,甚至更糟:忘记将其添加到注册表...
反射使得实现插件架构变得非常容易,其中插件 DLL 在运行时自动加载(在编译时没有显式链接)。
可以扫描这些以查找实现/扩展相关接口/类的类。然后可以使用反射来按需实例化这些实例。