C#中的反射是什么?

5

我最近从Java Web开发转为C#应用程序开发人员,主要开发WPF应用程序。以前我使用Java的Spring MVC,在那里很多代码结构都是预先设置好的。自从我转向WPF后,我的应用程序依赖于我设置可重用、解耦的代码。

我一直在努力提高自己的设计技能,包括泛型、设计模式和反射。

我很清楚这些都是什么,对于泛型和设计模式,我相当擅长应用最佳实践(虽然这还有待商榷)。

我不太熟悉的是反射。我知道反射是什么,也知道如何使用它来执行动态装配、方法和类调用等任务。

我不理解的是,这可能如何帮助我。

我经常听到如果你知道如何使用反射,它可以非常有帮助。但当我尝试研究这个话题时,我只能找到教程和参考,而不是它可以用于哪些方面。

我的问题是,作为WPF开发人员,我可以通过使用反射来做些什么,或者是否有一个地方或参考资料可以给我更多关于反射的实际应用和最佳实践,而不仅仅是语法的使用?


如果您需要其他信息,请告诉我,我在多个项目中实现了反射,主要是在您不知道一个对象有多少属性时非常有用。 - kobe
3
如果你正在问这个问题,那么它可能对你没有帮助。最佳实践是“只有在想不到其他方法时才使用反射”。 - Ben Voigt
大多数反射的用法现在都被“dynamic”关键字所取代。但它仍然非常有用,可以访问整洁的私有.NET方法。 - IS4
8个回答

4

反射可以用于以下方面:

  1. ORM (对象关系映射器):将对象的属性映射到数据库表中的列
  2. 模型绑定:将从Web表单提交的数据映射到对象的属性
  3. 复制:将业务对象映射到DTO
  4. 序列化:将对象映射到json或xml格式并进行转换
  5. MVC框架:检查URL并基于命名约定创建控制器对象
  6. 验证:检查对象的属性并强制执行验证规则

示例:

public class Customer : Entity
{
    [Required]
    public string Name { get; set; }
}

然后其他东西可以查找这些属性,比如验证器:

public class Validator
{
    public ValidationResult Validate(Entity entity)
    {
        // use reflection to find validation attributes and enforce them
    }
}

4
反射是一个强大的API。基本上,反射允许您:
- 检查程序集中包含的类型 - 检查给定类型的成员,包括元数据(通过属性) - 动态调用这些成员
如果您正在使用WPF中的绑定机制来绑定普通业务对象,那么您已经在使用反射了。绑定API在寻找更正式的绑定机制(从DependencyObject继承,具有自定义类型描述符等)之后,将使用反射获取和设置业务对象(或ViewModel)上的属性值。
例如,假设您有这个类 public class Customer { public string Name { get; set; } }
并且您的WPF控件(视图)绑定到这些Customer对象的集合,并具有绑定到Name属性的标签控件。所有这些都在XAML中定义,但在运行时,代码看起来像这样:
var value = dataObject.GetType().GetProperty("Name").GetValue(dataObject, null);

这将从绑定对象中获取值--而无需了解其类型--并允许控件以适当的方式显示它。同样:

dataObject.GetType().GetProperty("Name").SetValue(dataObject, "Bob", null);

将该对象的属性设置为"Bob",而不知道其类型。

至于它如何能够直接帮助您,这种用例相对较少。如果您发现自己需要使用反射来完成所需的工作,则可能要考虑重构代码;在静态类型语言(如C#)中,请使用遵循静态类型的代码,而不是使用反射进行动态调用。虽然元数据检查相当便宜,但通过反射调用成员比通过普通方式进行调用需要更多的处理。

简而言之,老实说,如果您正在询问它如何帮助您,最好假设 - 目前不能。


3

反射有点像匿名方法:在看到第一个用例之前,很难看出它有用处;但是一旦看到了第一个用例,通常就更容易理解了。

这里有一个我大约一年半前写的代码的实际例子。在我上大学时,我们为了玩德州扑克而用C#编写了一个机器人。我们还编写了一个基于事件的引擎来处理游戏本身,并包括网络支持,以允许来自任意数量不同机器的人类和计算机参与其中。为了让我们能够看到发生了什么,我们编写了一个单独的GUI应用程序,用于启动、加入和参与游戏。所有玩家都会通过这个应用程序添加,包括机器人和人类。

我们实现了我们的机器人根据模型行动,但是随着我们在项目中的进展,我们对该模型进行了更改,从而创建了不同版本的机器人。在我们的系统中,我们每个版本的模型都有一个类。

一旦你有了不同的版本,就很好比较它们是否有任何影响。为此,我们使用自定义属性标记了我们的每个计算机玩家类,我们称之为CPUAttribute

这就是反射发挥作用的地方:在启动时,我们的GUI将使用反射查看适当的程序集,并找到我们标记的所有类型。这些类型将被添加到下拉框中,当您创建玩家时,反射会用于实例化所选类型。


2

反射是计算机程序可以在运行时观察(进行类型内省)和修改自身结构和行为的过程。

反射可用于观察和/或修改程序在运行时的执行。反射导向的程序组件可以监视代码封闭区域的执行,并根据与该区域相关的期望目标自我修改。通常通过在运行时动态分配程序代码来实现此目的。

例如:

//Without reflection
Foo foo = new Foo();
foo.Hello();

//With reflection
object foo = Activator.CreateInstance(null, "Foo");
foo.GetType().GetMethod("Hello").Invoke(foo, null);

反射还可以用于动态地使给定程序适应不同的情况。例如,考虑一个使用两个不同的类X和Y来交替执行类似操作的应用程序。如果没有反射编程,该应用程序可能会硬编码调用类X和类Y的方法名称。然而,使用反射编程范例,可以设计和编写应用程序以利用反射来调用类X和类Y中的方法,而无需硬编码方法名称。为了利用更通用的代码执行,反射编程几乎总是需要额外的知识、框架、关系映射和对象相关性。只要使用反射编程,就可以避免硬编码。请参考MSDN文档,其中有关于此主题的详细信息和示例。但请记住,在混淆后,有时反射不起作用。

2
反射对于无法了解实际类型的代码非常有用。一个很好的例子就是你提到的WPF;它如何将xaml中的字符串解析为属性?以及xaml内部的数据绑定字符串?
经典案例包括:
  • 数据绑定
  • 序列化
  • ORM - 数据库<===>类
  • IDE / 设计工具 / 编辑器
  • 元编程
你的WPF使用至少其中3个;如果你使用数据库,则可能使用4个。
通常应避免在应用程序代码中过多使用反射;最好为您的领域实体使用接口。

1

我可以在这里给你举个例子。假设您有一个包含所有员工详细信息的表单。假设您有一个填充了所有属性的员工类对象。

在这种情况下,当您使用反射时,无需访问每个文本框并将值设置为文本框,您可以编写一个通用方法,该方法接受通用的员工对象并填写完整的表单。

这就是我使用反射的地方。

我甚至曾经使用反射来克隆一个对象,我不知道一个特定的类文件有多少属性,它是在运行时确定的,因此我使用反射来复制所有属性,这有点像克隆..

如果您有更多疑问,请告诉我。


1

在我看来,最好的方法是解决一个需要反射才能解决的问题 - 幸运的是,大多数问题实际上根本不需要使用反射,但当需要时它确实很方便。

我最近使用反射解决的问题是自定义XML序列化器/反序列化器,其中我有一些字符串类型注释和字符串值,并且正在使用反射从指定的字符串值在运行时重新创建类型(或类型列表)的实例。这需要:

  1. Activator.CreateInstance() 创建类型的通用列表实例
  2. 反射和使用TypeConverter 创建原始类型的实例
  3. 反射和使用MethodInfo 在运行时使用通用方法将原始类型添加到列表中。
在你已经熟练掌握的领域中寻找一个需要反思的问题,然后就去做吧——通过实践学习!

1

如果你在编译时就知道所有的东西,那么你不一定需要使用反射。正如其他人提到的,它可以用于自动构建UI、序列化、其他代码,否则这些代码只会成为类中多个字段重复执行相同操作的样板代码。

我曾经在需要在运行时加载动态生成的插件dll的情况下使用过它。然后,您可以查询程序集以获取从特定基类派生的类型,并实例化并使用来自插件dll的类型。

另一种使用(滥用?)反射的方法是绕过封装并在知道字段名称时设置另一个对象中的私有字段,尽管如果您这样做,您必须认真考虑是否值得这样做。


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