为什么我们需要反射?

Why we need Reflection at all?

我正在研究反射,我了解了一些,但我并没有了解与这个概念相关的所有内容。为什么我们需要反思?哪些我们无法实现但需要反思的事情?

反射支持的场景有很多,但我主要将它们分为两类。

  • 反射使我们能够编写代码分析其他代码

例如考虑有关程序集的最基本问题:其中有哪些类型?程序集是自描述的,反射是将该描述呈现给其他代码的机制。

例如,假设您要编写一个程序,该程序采用一个程序集,并以图形方式显示该程序集中各个 classes 之间的关系,以帮助您理解该代码。有这样的工具。他们在 Visual Studio。 有人写了那些工具。它们不是靠魔法出现的。反射是 .NET 框架中设计的机制,它使您或我或任何其他人能够编写理解代码的工具。

  • 反射使我们能够将编译时绑定移动到运行时。

假设您有一个静态方法 Foo.Bar()。当您在程序中调用 Foo.Bar() 时,您可以 100% 确定您认为将要调用的方法实际上将被调用。我们之所以调用静态方法 "static",是因为从名称 Bar 到被调用代码的绑定可以静态 理解——也就是说,没有 运行 程序。

现在考虑基于class 的虚方法Blah()。当您调用 whatever.Blah() 时,您不知道在编译时将调用哪个 Blah(),但您知道 some 方法 Blah() 没有arguments 将在 some 类型上调用,该类型是 whatever 的运行时类型,并且该类型等于或派生自声明 Blah() 的类型。 (其实你知道的更多:你知道它等于或派生自whatever的编译时类型。)虚拟绑定是动态绑定的一种形式,但它并不完全动态。用户无法决定此调用应该针对不同类型层次结构上的不同方法。

反射使我们能够在运行时根据用户的选择完全进行完全绑定的调用。我们付出了性能代价,并且失去了编译时类型安全性,但我们获得了灵活性,可以在运行时 100% 决定调用什么代码。在某些情况下,这是一个合理的权衡。

反射是 .NET 框架的一个很深的部分,您常常不知道自己在做它(例如,请参阅属性和 LINQ)。当您确实知道自己正在这样做时,即使感觉不对,这也可能是实现特定 objective.

的唯一方法

除了 Eric 在这里提到的两大领域之外,还有其他一些领域。还有很多,这些只是马上想到的一些。

序列化(和类似)

无论您是使用 XML 或 JSON 还是自己动手,当您不必为每个 class 编写特定代码以启用时,序列化对象会容易得多序列化。反射使您能够枚举对象中已标记为(或未标记为)序列化的属性并将它们写入输出。

但这与保存状态无关。反射允许我们编写也可以产生业务输出的通用方法,例如来自任意集合的 CSV 或 XLSX 文件。我从我的 ToCSV(...)ToExcel(...) 扩展中获得了很多好处,例如在我的基于 Web 的报告中生成可下载版本的数据集。

访问隐藏数据

是的,我知道,这是一个狡猾的人。是的,Eric 可能会为此打我一巴掌,但是...

那里有很多代码 - 我正在看着你,ASP.NET - 在 privateprotected 后面隐藏了有趣和有用的东西。有时,让它们消失的唯一方法是使用反射。有时这不是唯一的方法,但它可以是更简单的方法。

属性

每次将 Attribute 标记到您的 classes、方法等之一时,您就隐式提供了将通过反射访问的数据。想自己使用这些属性吗?反思是你获得它们的唯一途径。

LINQ 和其他表达式

这几天真的很重要。如果您曾经使用过 LINQ to SQL、Entity Frameworks 等,那么您已经以某种方式使用过 Expression。您编写一个简单的小 POCO 来表示数据库中的一行 table,其他所有内容都由反射处理。当您编写谓词表达式时,系统使用反射模型构建结构,然后处理(访问)这些结构以构建 SQL 语句。

表达式也不仅仅适用于 LINQ,一旦您知道自己在做什么,您就可以自己做一些非常有趣的事情。我有代码为 CSV 导入生成行解析器,当编译为 Func<string, TRecord> 时,运行 非常快。这些天我倾向于使用别人写的映射器,但当时我需要将一个包含 20K 条记录的文件定期上传到网站的总导入时间减少几个百分点。

P/Invoke 编组

这个在幕后很重要,偶尔也会出现在前台。当您想调用 Windows API 函数或使用本机 DLL 时,P/Invoke 为您提供了实现此目的的方法,而无需在两个方向上构建内存缓冲区。编组方法使用反射来翻译某些东西——字符串等是一个明显的例子——这样你就不必弄脏你的手了。全部基于作为反射基础的 Type 对象。


事实是,如果不进行反思,.NET 框架将不会是现在的样子。没有 Attributes,没有 Expressions,语言之间的互操作性可能要少得多。没有自动编组。没有 LINQ……至少在我们现在经常使用它的方式上是这样。