【问题标题】:Use-cases for reflection反射的用例
【发布时间】:2008-09-08 13:46:43
【问题描述】:

最近我在和一位同事谈论 C++,并感叹没有办法获取具有类字段名称的字符串并提取具有该名称的字段;换句话说,它缺乏反思。他困惑地看了我一眼,问我什么时候需要做这样的事情。

除了“嘿,我现在需要做”之外,我没有给他一个好的答案。所以我坐下来列出了一些我实际上用各种语言反射完成的事情。不幸的是,我的大部分示例都来自我使用 Python 进行的 Web 编程,我希望这里的人们能有更多示例。这是我想出的清单:

  1. 给定一个包含类似
    行的配置文件 x = "你好,世界!"
    y = 5.0
    动态设置某些config 对象的字段等于该文件中的值。 (这是我希望我能在 C++ 中做的,但实际上做不到。)

  2. 对对象列表进行排序时,根据配置文件或 Web 请求中给定属性名称的任意属性进行排序。

  3. 在编写使用网络协议的软件时,反射允许您根据来自该协议的字符串值调用方法。例如,我写了一个可以翻译的 IRC 机器人
    !some_command arg1 arg2
    进入方法调用 actions.some_command(arg1, arg2) 并打印该函数返回到 IRC 通道的任何内容。

  4. 当使用 Python 的 __getattr__ 函数(有点像 Ruby/Smalltalk 中的 method_missing)时,我正在使用一个包含大量统计信息的类,例如 late_total。对于每个统计数据,我希望能够添加 _percent 以将该统计数据作为我计算的总事物的百分比(例如,stats.late_total_percent)。反射使这变得非常容易。

那么,这里的任何人都可以从他们自己的编程经验中举出一些例子吗?下次有同事问我为什么“曾经想做这样的事情”时,我想做好更多准备。

【问题讨论】:

    标签: reflection


    【解决方案1】:

    我可以列出以下反射用法:

    • 后期绑定
    • 安全性(出于安全原因自省代码)
    • 代码分析
    • 动态输入(没有反射就无法输入鸭子)
    • 元编程

    根据我的个人经验,一些实际的反射用法:

    • 基于反射开发的插件系统
    • 使用了面向方面的编程模型
    • 执行静态代码分析
    • 使用了各种依赖注入框架
    • ...

    反思是件好事 :)

    【讨论】:

      【解决方案2】:

      我使用反射来获取异常、日志记录等的当前方法信息。

      string src = MethodInfo.GetCurrentMethod().ToString();
      string msg = "Big Mistake";
      Exception newEx = new Exception(msg, ex);
      newEx.Source = src;
      

      而不是

      string src = "MyMethod";
      string msg = "Big MistakeA";
      Exception newEx = new Exception(msg, ex);
      newEx.Source = src;
      

      复制/粘贴继承和代码生成更容易。

      【讨论】:

      • 究竟什么是“复制/粘贴继承”?而且你的异常中没有堆栈跟踪吗?
      • 在我使用这个的情况下,异常最终被记录到一个文本文件并且没有记录堆栈跟踪。
      • 不要在生产代码中使用这种方法。在发布版本中,由于内联和方法链接(即尾调用优化),堆栈可以并且将会被折叠。如果您记录异常,则无论如何您都有堆栈跟踪(没有理由手动添加源代码)。谨慎使用GetCurrentMethod,仅在调试版本中使用。
      【解决方案3】:

      我现在的情况是,我有一个 XML 流通过网络传入,我需要实例化一个 Entity 对象,该对象将从流中的元素填充自身。使用反射来确定哪个实体对象可以处理哪个 XML 元素比编写一个庞大的、维护噩梦的条件语句更容易。 XML 模式与我如何构建和命名对象之间显然存在依赖关系,但我控制了这两者,所以这不是一个大问题。

      【讨论】:

        【解决方案4】:

        很多时候,您希望动态实例化并使用直到运行时才知道类型的对象。例如使用 OR-mappers 或在插件架构中。如果您想编写日志库并动态检查异常的类型和属性,则模拟框架会使用它。

        如果我想得更久一点,我可能会想出更多的例子。

        【讨论】:

          【解决方案5】:

          如果输入数据(如 xml)具有很容易映射到对象实例的复杂结构,或者我需要实例之间的某种“是”关系,我发现反射非常有用。

          由于反射在 java 中相对容易,我有时将它用于简单的数据(键值映射),其中我有一组固定的小键。一方面很容易确定一个键是否有效(如果该类有一个设置器 setKey(String data)),另一方面我可以更改(文本)输入数据的类型并隐藏转换(例如简单转换在 getKey()) 中转换为 int,因此应用程序的其余部分可以依赖正确键入的数据。 如果某个对象的某些键值对的类型发生变化(例如,将 int 形式更改为浮点数),我只需在数据对象及其用户中更改它,但不必记住也要检查解析器.如果性能是一个问题,这可能不是一个明智的方法......

          【讨论】:

            【解决方案6】:

            编写调度程序。 Twisted 使用 python 的反射功能来调度 XML-RPC 和 SOAP 调用。 RMI 使用 Java 的反射 API 进行调度。

            命令行解析。根据传入的命令行参数构建配置对象。

            在编写单元测试时,使用反射会很有帮助,尽管我主要使用它来绕过访问修饰符 (Java)。

            【讨论】:

              【解决方案7】:

              当我想访问框架或第三方库中有一些内部或私有方法时,我在 C# 中使用了反射。

              (免责声明:这不一定是最佳实践,因为私有和内部方法可能会在以后的版本中更改。但它可以满足我的需要。)

              【讨论】:

                【解决方案8】:

                嗯,在静态类型的语言中,您需要在需要执行“动态”操作的任何时候使用反射。它可用于工具目的(扫描对象的成员)。在 Java 中,它大量用于 JMX 和动态代理。而且有很多一次性的情况,它确实是唯一的方法(几乎任何时候你需要做一些编译器不允许你做的事情)。

                【讨论】:

                  【解决方案9】:

                  我通常使用反射进行调试。与各种打印语句相比,反射可以更轻松、更准确地显示系统内的对象。在许多具有一流函数的语言中,您甚至可以调用对象的函数而无需编写特殊代码。

                  然而,有一种方法可以做你想做的事(ed)。使用哈希表。存储针对字段名称键入的字段。

                  如果您真的愿意,您可以创建标准的 Get/Set 函数,或创建动态执行此操作的宏。 #define GetX() Get("X") 之类的。

                  您甚至可以通过这种方式实现自己的不完美反射。

                  对于高级用户,如果您可以编译代码,则可以启用调试输出生成并使用它来执行反射。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2012-04-06
                    • 2016-04-16
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2018-12-12
                    • 2015-07-22
                    相关资源
                    最近更新 更多