最近,我与一位同事谈论 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)。反思使这变得非常容易。

那么,这里有人可以从他们自己的编程经验中举出一些例子,说明反思是有帮助的吗?下次当同事问我为什么“想做那样的事情”时,我希望做好更充分的准备。

有帮助吗?

解决方案

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

  • 后期绑定
  • 安全性(出于安全原因检查代码)
  • 代码分析
  • 动态类型(没有反射就不可能进行鸭子类型)
  • 元编程

根据我个人的经验,反射在现实世界中的一些用法:

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

反思是件好事:)

其他提示

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

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;

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

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

很多时候,您想要动态实例化并使用直到运行时才知道类型的对象。例如,使用 OR 映射器或插件架构。如果您想编写一个日志库并动态地检查异常的类型和属性,则模拟框架可以使用它。

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

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

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

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

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

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

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

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

好吧,在静态类型语言中,每当需要执行“动态”操作时,您都希望使用反射。它对于工具目的(扫描对象的成员)非常有用。在 Java 中,它在 JMX 和动态代理中使用较多。在很多一次性情况下,它实际上是唯一的方法(几乎任何时候你需要做一些编译器不允许你做的事情)。

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

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

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

你甚至可以用这种方式实现你自己不完美的反射。

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top