C#反思的理由

时间:2008-09-06 21:10:38

标签: c# .net reflection

我想知道C#代码中反射的适当性。例如,我编写了一个函数,它迭代给定源对象的属性并创建指定类型的新实例,然后将具有相同名称的属性值从一个复制到另一个。我创建了这个来将数据从一个自动生成的LINQ对象复制到另一个,以避免LINQ中多个表缺少继承。

然而,我不禁想到这样的代码真的是“作弊”,即不是使用提供的语言结构来实现给定的结束,它允许你绕过它们。

这种代码在多大程度上可以接受?有什么风险?这种方法的合法用途是什么?

6 个答案:

答案 0 :(得分:8)

有时候使用反射可能有点像黑客,但很多时候它只是最神奇的代码工具。

查看.Net属性网格 - 任何使用Visual Studio的人都会熟悉它。您可以将它指向任何对象,它将生成一个简单的属性编辑器。这使用反射,事实上VS的工具箱大部分都是。

查看单元测试 - 它们是通过反射加载的(至少在NUnit和MSTest中)。

Reflection允许静态语言的动态风格行为。

它真正需要的一件事是鸭子打字--C#编译器已经支持这个:你可以foreach看起来像IEnumerable的任何东西,无论它是否实现了接口。您可以在具有名为Add的方法的任何类上使用C#3集合语法。

在需要动态样式行为的地方使用反射 - 例如,您有一组对象,并且您希望在每个对象上检查相同的属性。

动态类型的风险类似 - 编译时异常变为运行时异常。您的代码不是“安全”,您必须做出相应的反应。

.Net反射代码非常快,但没有显式调用那么快。

答案 1 :(得分:2)

我同意,它给了我它有效,但感觉就像一个黑客的感觉。尽可能避免反思。在重构了具有反射功能的代码之后,我已经被多次烧毁。代码编译很好,测试甚至可以运行,但在特殊情况下(测试没有覆盖),程序会因为我对重构代码中的一个对象进行重构而耗尽运行时间。

示例1: OR映射器中的反射,您可以更改对象模型中属性的名称或类型:打开运行时。

示例2:您在SOA商店。 Web服务完全解耦(或者您认为)。他们有自己的一组生成的代理类,但是在映射中你决定节省一些时间并且你这样做:

ExternalColor c = (ExternalColor)Enum.Parse(typeof(ExternalColor), 
                                            internalColor.ToString());

在幕后,这也是反思,但是由.net框架本身完成。如果您决定将 InternalColor.Grey 重命名为 InternalColor.Gray ,会发生什么?一切看起来都不错,它构建得很好,甚至可以正常运行......直到有一天,一些愚蠢的用户决定使用灰色......此时映射器会爆炸。

答案 2 :(得分:2)

反思是一个很棒的工具,我不能没有。它可以使编程更容易,更快捷。

例如,我在ORM层中使用反射,以便能够从表中分配具有列值的属性。如果它不是用于反射,我必须为每个表/类映射创建一个复制类。

至于上面的外部颜色异常。问题不在于Enum.Parse,而是编码器没有捕获到正确的异常。由于解析了字符串,编码器应始终假定字符串可能包含不正确的值。

同样的问题适用于.Net中的所有高级编程。 “拥有权利的同时也被赋予了重大的责任”。使用反射可以为您提供更多动力但请确保您知道如何正确使用它。网上有很多例子。

答案 3 :(得分:1)

可能只是我,但我进入这个方法的方法是创建一个代码生成器 - 在运行时使用反射有点昂贵和无类型。创建将根据您的最新代码生成的类并以强类型方式复制所有内容将意味着您将在构建时捕获这些错误。

例如,生成的类可能如下所示:

static class AtoBCopier
{
    public static B Copy(A item)
    {
        return new B() { Prop1 = item.Prop1, Prop2 = item.Prop2 };
    }
}

如果任何一个类没有属性或其类型发生更改,则代码不会编译。此外,时间也有很大改善。

答案 4 :(得分:1)

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

答案 5 :(得分:0)

反射使得实现插件体系结构非常容易,其中插件DLL在运行时自动加载(在编译时未明确链接)。

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