C#中反射的理由

10

我一直在考虑在C#代码中添加反射是否合适。例如,我编写了一个函数,该函数遍历给定源对象的属性,并创建指定类型的新实例,然后将相同名称的属性值从一个对象复制到另一个对象。我创建这个函数是为了将数据从一个自动生成的LINQ对象复制到另一个对象,以解决LINQ中缺少多个表的继承的问题。

然而,我仍认为像这样的代码实际上是“作弊”,即不使用提供的语言结构来实现特定目标,而是允许您规避它们。

这种代码的可接受程度是多少?有哪些风险?这种方法的合法用途是什么?

6个回答

8
有时候使用反射可能有些取巧,但大多数情况下它是最好的代码工具。
看看.Net属性网格 - 任何使用Visual Studio的人都会熟悉它。您可以将其指向任何对象,它会生成一个简单的属性编辑器。实际上,这使用了反射,事实上,大多数VS的工具箱都使用了反射。
看看单元测试 - 它们通过反射加载(至少在NUnit和MSTest中)。
反射允许静态语言具有动态风格的行为。
它真正需要的是鸭子类型 - C#编译器已经支持这一点:您可以foreach任何看起来像IEnumerable的东西,无论它是否实现了接口。您可以在任何具有名为Add的方法的类上使用C#3集合语法。
在需要动态风格行为的任何地方使用反射 - 例如,您有一个对象集合,您想在每个对象上检查相同的属性。
动态类型的风险与动态类型类似 - 编译时异常变成了运行时异常。您的代码不太“安全”,您必须做出相应的反应。
.Net反射代码非常快,但不如显式调用快。

如果有人问我反射的优缺点是什么,这个回答合适吗? - Peter PitLock

3
我同意,它让我感觉像是一种“权宜之计”的方案。如果有可能,我会尽量避免使用反射。在对使用反射的代码重构后,我曾多次遭受过失败的经历。尽管代码可以正常编译,测试也能顺利运行,但在特定情况下(测试无法覆盖的情况下)程序会在运行时因为我在其中一个对象中进行的反射代码操作而崩溃。 例子1: 在OR映射器中使用反射,当你更改了对象模型中属性的名称或类型时,程序将在运行时崩溃。 例子2: 你在一个SOA商店里工作,Web服务是完全解耦的(至少你认为如此)。他们拥有自己生成的代理类,但在映射过程中,为了节省时间,你决定这样做:
ExternalColor c = (ExternalColor)Enum.Parse(typeof(ExternalColor), 
                                            internalColor.ToString());

在底层,这也是反射,但由 .net 框架本身完成。现在,如果您决定将 InternalColor.Grey 重命名为 InternalColor.Gray,会发生什么?一切看起来都很好,构建也很好,甚至运行良好... 直到某一天,一些愚蠢的用户决定使用灰色...此时映射器将崩溃。


2

反射是一种非常有用的工具,没有它我无法生存。它可以使编程变得更加容易和快速。

例如,我在我的ORM层中使用反射来将属性赋值为来自表格的列值。如果没有反射,我就必须为每个表/类映射创建一个副本类。

至于上面提到的外部颜色异常。问题不在于Enum.Parse,而是程序员没有捕获正确的异常。由于解析了一个字符串,程序员应该始终假设该字符串可能包含不正确的值。

同样的问题也适用于.Net中的所有高级编程。"伴随着巨大的力量,必然会有巨大的责任"。使用反射可以给你带来很多力量。但请确保您知道如何正确地使用它。网上有数十个例子。


1
也许只是我个人的看法,但我会通过创建代码生成器来实现这一点 - 在运行时使用反射有点昂贵且不可靠。创建根据最新代码生成的类,并以强类型方式复制所有内容,这意味着您将在构建时捕获这些错误。
例如,生成的类可能如下所示:
static class AtoBCopier
{
    public static B Copy(A item)
    {
        return new B() { Prop1 = item.Prop1, Prop2 = item.Prop2 };
    }
}

如果任何一个类都没有这些属性或它们的类型发生了变化,代码就无法编译。此外,时间方面也有巨大的改进。


1
我最近在C#中使用了反射来查找特定接口的实现。我编写了一个简单的批处理式解释器,根据类名查找每个计算步骤的“操作”。然后反射当前命名空间,弹出可以执行Execute()的IStep接口的正确实现。这样,添加新的“操作”就像创建一个新的派生类一样容易 - 不需要将其添加到注册表中,更糟糕的是:忘记将其添加到注册表中...

0

反射使得实现插件架构变得非常容易,其中插件DLL可以在运行时自动加载(不需要在编译时显式链接)。

可以扫描这些DLL中实现/扩展相关接口/类的类。然后可以使用反射按需实例化这些类的实例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接