为什么我们需要反射机制?

4

我正在学习反射,对于这个概念我有一些了解,但并不是全部。我们为什么需要反射?有哪些事情是我们无法通过其他方式实现,必须使用反射来完成的呢?


一个情况是...如果你想基于一个字符串创建一个对象。 - user2404597
我正在写一些好的东西来给你举例。作为一名程序员,我几乎一直在使用反射。 - Mário Santos
使用线程、泛型和事件(监听器、事件总线、C# 事件、可观察等),您可以创建框架,将您的代码减少到仅包含业务特征。 - Mário Santos
只是一个例子:想象一下,您有一个类具有1000个属性,并且您有一个方法clear(private void clear())。 - Mário Santos
它应该获取所有原始的int、short、double类型并将其设置为0,以及Integer、Short、Double、Boolean、String类型并将其设置为null。 - Mário Santos
你能想象需要编写的代码量吗?使用反射,您可以编写更少的代码。 - Mário Santos
2个回答

18

反射提供了很多场景,但我主要将它们分为两个类别:

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

例如,考虑一个有关程序集最基本问题:其中有哪些类型?程序集是自描述的,而反射是使该描述浮出水面并可用于其他代码的机制。

例如,假设您想编写一个程序,该程序获取一个程序集并在图形界面中显示该程序集中各个类之间的关系,以帮助您理解代码。有这样的工具,它们在Visual Studio中。 某人编写了这些工具。 它们不是通过魔法出现的。 反射是设计到.NET框架中的机制,使您或我或任何其他人都能编写理解代码的工具。

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

假设您有一个静态方法Foo.Bar()。当您在程序中调用Foo.Bar()时,您可以百分之百确定会调用您认为将要被调用的方法。 我们称静态方法为“静态”,因为可以静态地理解名称Bar与将被调用的代码之间的绑定,即在不运行程序的情况下。

现在考虑一个基类上的虚方法Blah()。当你调用whatever.Blah()时,你不知道编译时确切调用哪个Blah(),但你知道将在运行时类型为whatever的某个类型上调用没有参数的某个方法Blah(),并且该类型等于或派生自声明Blah()的类型。(实际上你知道更多: 知道它等于或派生自whatever的编译时类型。)虚绑定是动态绑定的一种形式,但它不是完全动态的。用户无法决定这个调用应该在不同类型层次结构上的不同方法中进行调用。
反射使我们能够进行完全基于运行时、完全基于用户选择的绑定调用。我们会付出性能代价,失去编译时类型安全,但我们获得了在运行时决定调用什么代码的灵活性。在某些情况下,这是一个合理的折衷方案。

5
反射是.NET框架的深度组成部分,通常您并不知道自己在使用它(例如属性和LINQ)。即使您知道自己在使用它,即使感觉不对,它也可能是实现特定目标的唯一方法。除了Eric提到的两个广泛领域外,还有其他几个领域。还有很多,这只是立即想到的一些。序列化(及类似内容):无论您使用XML、JSON还是自己编写,当您不必为每个类编写特定代码以启用序列化时,序列化对象都会变得更加容易。反射使您能够枚举已被标记为(或未被标记为)序列化的对象中的属性,并将它们写入输出。这不是关于保存状态的。反射允许我们编写通用方法,可以从任意集合中生成业务输出,例如CSV或XLSX文件。我经常使用我的ToCSV(...)和ToExcel(...)扩展来生成可下载的数据集版本,用于基于Web的报告。访问隐藏数据:是的,我知道,这是一个棘手的问题。是的,Eric可能会因此责骂我,但是...有很多代码——我在看你,ASP.NET——将有趣和有用的东西隐藏在private或protected后面。有时获取它们的唯一方法是使用反射。有时这不是唯一的方法,但它可能是更简单的方法。属性:每次您在其中一个类、方法等上标记Attribute时,都会隐含地提供数据,该数据将通过反射进行访问。想要自己使用这些属性吗?反射是您唯一可以使用它们的方法。LINQ和其他表达式:这些天真的重要。如果您曾经使用过LINQ to SQL、Entity Frameworks等,则以某种方式使用了Expression。您编写一个简单的POCO来表示数据库表中的一行,其余所有内容都由反射处理。当您编写谓词表达式时,系统正在使用反射模型构建结构,然后对其进行处理(访问)以构建SQL语句。表达式不仅适用于LINQ,一旦您知道自己在做什么,还可以自己做一些非常有趣的事情。我有代码来生成CSV导入的行解析器,当编译到Func时运行得非常快。这些天我倾向于使用其他人编写的映射器,但当时我需要从定期上传到网站的文件中切割总导入时间的几个百分点,因为该文件包含20K条记录。P/Invoke Marshalling。
这是一个在幕后和偶尔在前台都很重要的问题。当你想调用Windows API函数或使用本地DLL时,P/Invoke提供了方法来实现这一点,而不必在双向构建内存缓冲区时进行操作。这些封送方法使用反射来翻译某些内容 - 字符串等显然是其中的例子 - 这样你就不必弄脏你的手了。所有这些都基于反射的基础——Type对象。
事实上,如果没有反射,.NET框架就不会成为现在的样子。没有Attribute、没有Expression,可能语言之间的互操作性会少很多。没有自动封送。没有LINQ...至少不是我们现在经常使用的方式。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接