代码之家  ›  专栏  ›  技术社区  ›  JPCosta

反射究竟是什么?什么时候是一种好方法?

  •  17
  • JPCosta  · 技术社区  · 17 年前

    反射究竟是什么? 我读了维基百科关于这个主题的文章,我理解它是一种元编程,程序可以在运行时修改自己,但这意味着什么? 在什么情况下,这是一种好的方法,什么时候最好使用它?

    11 回复  |  直到 17 年前
        1
  •  24
  •   ConcernedOfTunbridgeWells    15 年前

    反射是一种功能,您可以在运行时查询对象的属性。例如,Python、Java和.NET具有可以找到对象的实例变量或方法的工具。

    反射应用程序的一个例子是O/R映射层。有些使用反射来构造对象,方法是在运行时查询其属性并动态填充实例。这允许您根据某种数据字典中的元数据进行编程,而无需重新编译应用程序。

    举一个简单的例子,我将使用Python,因为它的反射设备使用起来非常简单,并且涉及到比Java或.NET的更少的样板。

    ActivePython 2.5.2.2 (ActiveState Software Inc.) based on
    Python 2.5.2 (r252:60911, Mar 27 2008, 17:57:18) [MSC v.1310 32 bit (Intel)] on
    win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> class foo:
    ...     def __init__(self):
    ...             self.x = 1
    ...
    >>> xx = foo()      # Creates an object and runs the constructor
    >>> xx.__dict__     # System metadata about the object
    {'x': 1}
    >>> a = xx.__dict__ # Now we manipulate the object through 
    >>> a['y'] = 2      # its metadata ...
    >>> print xx.y      # ... and suddenly it has a new instance variable
    2                   
    >>>
    

    现在,我们使用基本反射来检查任意对象的实例变量。特殊变量 __dict__ 在python上是一个对象的系统属性,该对象的成员散列表由变量(或方法)名称键控。我们已经对对象进行了反射检查,并使用反射工具人为地将第二个实例变量插入其中,然后我们可以通过将其作为实例变量调用来显示该变量。

    注意,这个特定的技巧在Java或.NET上不起作用,因为实例变量是固定的。这些语言的类型系统不允许在运行时像Python的“duck”类型系统那样添加新的实例变量。但是,您可以反射性地更新类型定义中声明的实例变量的值。

    您还可以使用反射动态地构造方法调用,并执行各种其他简单的技巧,例如基于参数实例化对象。例如,如果您有某种基于插件的系统,其中某些功能是可选的,那么您可以使用反射来查询插件提供的服务(可能通过查询某些接口是否实现),而不需要显式元数据。

    许多动态语言接口(如OLE自动化)使用反射作为接口的一个组成部分。

        2
  •  20
  •   Jon Skeet    17 年前

    没什么大不了的 修改 在执行时执行代码,但检查对象并要求它们在不静态了解其类型的情况下执行代码。

    描述它的一个简单方法是“使静态类型语言动态运行的一种有点痛苦的方法”。

    编辑:用途:

    • 配置(例如,获取指定类型和属性的XML文件,然后构造适当的对象)
    • 测试(由名称或属性标识的单元测试)
    • Web服务(至少在.NET中,核心Web服务引擎中使用了大量反射)
    • 自动事件连接-提供具有适当名称的方法,例如 SubmitButton_Click ASP.NET将把该方法作为 SubmitButton Click 事件(如果已打开自动布线)

    这是个好主意吗?好吧,只有在选择是痛苦的时候。我更喜欢静态类型,当它不妨碍的时候-然后你会得到很多编译时的好处,而且速度也更快。但是当你 需要它,反思让你做各种事情,否则就是不可能的。

        3
  •  3
  •   Marc W    17 年前

    我能想到的第一个很好的例子是,当你需要在一个给定的对象上执行一组方法而不知道在编译时会有什么方法存在的时候。

    以单元测试框架为例。负责运行所有单元测试的测试运行程序类不会提前知道您要命名的方法。它所知道的是,它们将被前缀“test”(或在Java 5的情况下,注释)。 @Test )因此,当给定一个测试类时,它会反映在该类上,以便获得其中所有方法的列表。然后,它以字符串的形式迭代这些方法名,如果这些方法以“test”开头,则在对象上调用它们。没有反省是不可能的。这只是一个例子。

        4
  •  3
  •   Scott Arrington    17 年前

    在我能想到的至少一个项目中,思考对我很有用。我们编写了一个内部“流程管理器”程序,在特定的时间间隔内执行许多关键业务流程。这个项目的建立使得核心实际上只是一个Windows服务,它的计时器对象每隔30秒左右就会触发一次,并检查要做的工作。

    实际工作是在类库中完成的(适当地称为“workerlib”)。我们用执行特定任务的公共方法定义类(移动文件、将数据上载到远程站点等),其思想是核心服务可以从工作库调用方法,而不知道它调用的方法。这允许我们在数据库中为作业创建一个时间表,甚至可以在不必更改核心系统的情况下将新方法添加到类库中。

    基本思想是,我们可以在核心服务中使用反射来执行方法,这些方法的名称存储在定义调度的数据库中。它在实践中非常整洁。我们的核心服务是可靠的,可以根据需要执行任务,而实际的工人图书馆可以根据需要进行扩展和更改,而无需核心甚至关怀。

    如果您需要进一步解释,请随时询问。这只是我能想到的最好的解释现实世界场景的方法,在这个场景中,反思真的让事情变得更容易。

        5
  •  2
  •   Aric TenEyck    17 年前

    另一个示例:我使用的代码获取数据库的输出(这是一组具有命名列的行),并将其馈送到对象数组中。我迭代这些行,如果目标对象有一个具有相同名称和类型的属性,我就设置它。这使得我的数据获取代码看起来像这样:

    SqlDataReader sdr = Helper.GetReader("GetClientByID", ClientID);
    Client c = new Client();
    FillObject(sdr, c);
    return c;
    
        6
  •  2
  •   kemiller2002    17 年前

    实际上,反射应该被认为是一种代码放大器。它可以使好的代码更好、更干净,坏的代码更糟。它是做什么的?它确实允许您编写代码,您不完全确定在编写代码时要做什么。您有一个一般的想法,但是它不允许您在编译程序时对要执行的对象、方法和属性进行编码。

    其他文章说它允许程序根据配置值执行代码是正确的,但真正的力量是它允许您严重地弯曲面向对象编程的规则。这就是它真正的作用。这有点像关闭安全措施。私有方法和属性可以通过反射和其他任何方法访问。

    MS使用反射的一个很好的例子是数据对象上的数据绑定。为要绑定到下拉列表等的对象指定文本字段和数据字段的名称,代码反映对象并提取适当的信息。数据绑定对象一次又一次地执行相同的过程,但它不知道必须与哪种类型的对象绑定。反射是编写少量代码以处理所有可能的情况的一种方便方法。

        7
  •  1
  •   Gandalf    17 年前

    在Java中,它基本上是一种在事先不知道它的情况下实例化类的方法。假设您希望用户能够通过添加希望程序使用的类来更改配置文件(假设您有许多接口实现)。通过反射,您可以基于对象的名称、方法签名等创建对象。,然后将其转换到您的接口。

        8
  •  1
  •   Tetsujin no Oni    17 年前

    反射对于运行时配置很有用,它允许通过外部配置驱动系统的某些部分。

    例如,类工厂可以基于输入文件构造不同的具体类型,其中具体类型需要不同的配置信息来调用具体的构造函数,而不是使用生成器接口。(使用反射定位的对象的构造函数方法)。

        9
  •  1
  •   Sean    17 年前

    反射是(基本上)程序查询编译器可用类型信息的能力。例如,给定一个类型的名称,就可以查询它包含的方法。然后,对于每个方法,您可以查询它们所采用的参数类型等。

    它对于运行时配置很有用,因为在运行时配置文件中指定了应用程序的行为。配置可能包含您应该使用的具体类型的名称(IOC容器通常也是这样)。使用反射,您可以创建此具体类型的实例(通过反射API)并使用它。

        10
  •  1
  •   ChrisF    17 年前

    我给你举个例子。

    作为编程练习,我写了一个MP3文件检查程序。它扫描我的音乐库并显示我对DataGridView感兴趣的ID1/ID2标记。我使用反射从mp3信息类中获取属性,而不需要让UI代码知道关于该类的任何信息。如果要更改显示的信息,可以编辑MP3信息类或更改其配置(取决于我如何编写该类),而不必更新UI。

    这也意味着我可以通过交换数据库类,使用依赖注入来从末尾使用相同的内容来显示有关数字照片的信息。

        11
  •  1
  •   Abid Ali    15 年前

    程序集包含模块,模块包含类型,类型包含成员。反射提供封装程序集、模块和类型的对象。可以使用反射动态创建类型的实例、将类型绑定到现有对象或从现有对象获取类型。然后可以调用类型的方法或访问其字段和属性。反射的典型用途包括:

    • 使用程序集定义和加载程序集,加载程序集清单中列出的模块,并从此程序集定位类型并创建其实例。
    • 使用模块查找包含模块和模块中类的程序集等信息。您还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。
    • 使用constructorinfo可以发现信息,如构造函数的名称、参数、访问修饰符(如public或private)和实现细节(如abstract或virtual)。使用类型的getconstructors或getconstructor方法来调用特定的构造函数。
    • 使用MethodInfo可以发现方法的名称、返回类型、参数、访问修饰符(如public或private)和实现细节(如abstract或virtual)等信息。使用类型的GetMethods或GetMethod方法调用特定方法。
    • 使用fieldinfo可以发现字段的名称、访问修饰符(如public或private)和实现细节(如static)等信息,并获取或设置字段值。
    • 使用eventinfo可以发现事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等信息,还可以添加或删除事件处理程序。
    • 使用propertyinfo可以发现诸如名称、数据类型、声明类型、反射类型以及属性的只读或可写状态等信息,还可以获取或设置属性值。
    • 使用ParameterInfo可以发现参数的名称、数据类型、参数是输入参数还是输出参数以及参数在方法签名中的位置等信息。