【问题标题】:Justification for Reflection in C#C# 中反射的理由
【发布时间】:2008-09-06 21:10:37
【问题描述】:

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

但是,我不禁认为这样的代码确实是“作弊”,也就是说,它允许您规避它们,而不是使用提供的语言结构来实现给定的目的。

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

【问题讨论】:

    标签: c# .net reflection


    【解决方案1】:

    有时使用反射可能有点小技巧,但很多时候它只是最棒的代码工具。

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

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

    反射允许静态语言的动态样式行为。

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

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

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

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

    【讨论】:

    • 如果有人问我反思的优点和缺点是什么,这是一个合适的答案吗?
    【解决方案2】:

    我同意,它给了我它的工作原理,但感觉就像一个 hack 的感觉。我尽量避免反思。在重构包含反射的代码后,我已经被烧毁了很多次。代码编译得很好,测试甚至可以运行,但是在特殊情况下(测试没有涵盖),由于我在反射代码插入的对象之一中进行了重构,程序会破坏运行时。

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

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

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

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

    【讨论】:

      【解决方案3】:

      反射是一个很棒的工具,我离不开它。它可以使编程更容易和更快。

      例如,我在我的 ORM 层中使用反射来为属性分配表中的列值。如果不是为了反射,我必须为每个表/类映射创建一个副本类。

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

      同样的问题适用于 .Net 中的所有高级编程。 “拥有权利的同时也被赋予了重大的责任”。使用反射会给你很大的力量。但请确保您知道如何正确使用它。网上有几十个例子。

      【讨论】:

        【解决方案4】:

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

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

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

        如果任一类没有属性或其类型发生变化,则代码不会编译。另外,时间有了很大的改进。

        【讨论】:

          【解决方案5】:

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

          【讨论】:

            【解决方案6】:

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

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-07-26
              • 1970-01-01
              • 2023-03-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多