反射如何不会导致代码异味? [英] How could Reflection not lead to code smells?

查看:42
本文介绍了反射如何不会导致代码异味?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我来自低级语言 - C++ 是我编程的最高级别.

I come from low level languages - C++ is the highest level I program in.

最近我遇到了 Reflection,我无法理解如何在没有代码异味的情况下使用它.

Recently I came across Reflection, and I just cannot fathom how it could be used without code smells.

在我看来,在运行时检查类/方法/函数的想法指出了设计中的一个缺陷——我认为反射(试图)解决的大多数问题都可以与多态或正确使用继承一起使用.

The idea of inspecting a class/method/function during runtime, in my opinion, points to a flaw in design - I think most problems Reflection (tries to) solve could be used with either Polymorphism or proper use of inheritance.

我错了吗?我是否误解了 Reflection 的概念和效用?

Am I wrong? Do I misunderstand the concept and utility of Reflection?

我正在寻找一个很好的解释,说明何时使用反射,而其他解决方案将失败或实施起来过于繁琐,以及何时不使用它.

I am looking for a good explanation of when to utilize Reflection where other solutions will fail or be too cumbersome to implement as well as when NOT to use it.

请赐教这个低级的lubber.

Please enlighten this low-level lubber.

推荐答案

反射最常用于规避静态类型系统,但它也有一些有趣的用例:

Reflection is most commonly used to circumvent the static type system, however it also has some interesting use cases:

让我们写一个 ORM!

如果您熟悉 NHibernate 或大多数其他 ORM,您可以编写映射到数据库表的类,如下所示:

If you're familiar with NHibernate or most other ORMs, you write classes which map to tables in your database, something like this:

// used to hook into the ORMs innards
public class ActiveRecordBase
{
    public void Save();
}

public class User : ActiveRecordBase
{
    public int ID { get; set; }
    public string UserName { get; set; }
    // ...   
}

您认为 Save() 方法是如何编写的?好吧,在大多数 ORM 中,Save 方法不知道派生类中有哪些字段,但它可以使用反射访问它们.

How do you think the Save() method is written? Well, in most ORMs, the Save method doesn't know what fields are in derived classes, but it can access them using reflection.

完全可以以类型安全的方式具有相同的功能,只需要求用户覆盖将字段复制到数据行对象的方法,但这会导致大量样板代码和膨胀.

Its wholly possible to have the same functionality in a type-safe manner, simply by requiring a user to override a method to copy fields into a datarow object, but that would result in lots of boilerplate code and bloat.

存根!

Rhino Mocks 是一个模拟框架.您将接口类型传递给方法,框架将在幕后动态构造和实例化实现该接口的模拟对象.

Rhino Mocks is a mocking framework. You pass an interface type into a method, and behind the scenes the framework will dynamically construct and instantiate a mock object implementing the interface.

当然,程序员可以手动为模拟对象编写样板代码,但是如果框架会为她做这件事,她为什么要这样做呢?

Sure, a programmer could write the boilerplate code for the mock object by hand, but why would she want to if the framework will do it for her?

元数据!

我们可以用属性(元数据)装饰方法,它可以用于多种目的:

We can decorate methods with attributes (metadata), which can serve a variety of purposes:

[FilePermission(Context.AllAccess)]    // writes things to a file
[Logging(LogMethod.None)]              // logger doesn't log this method
[MethodAccessSecurity(Role="Admin")]   // user must be in "Admin" group to invoke method
[Validation(ValidationType.NotNull, "reportName")] // throws exception if reportName is null
public void RunDailyReports(string reportName) { ... }

您需要反思检查属性的方法.大多数用于.NET的AOP框架使用属性进行策略注入.

You need to reflect over the method to inspect the attributes. Most AOP frameworks for .NET use attributes for policy injection.

当然,您可以编写相同类型的内联代码,但这种风格更具声明性.

Sure, you can write the same sort of code inline, but this style is more declarative.

让我们做一个依赖框架吧!

许多 IoC 容器需要一定程度的反射才能正常运行.例如:

Many IoC containers require some degree of reflection to run properly. For example:

public class FileValidator
{
    public FileValidator(ILogger logger) { ... }
}

// client code
var validator = IoC.Resolve<FileValidator>();

我们的 IoC 容器将实例化一个文件验证器并将 ILogger 的适当实现传递给构造函数.哪个实现?这取决于它的实施方式.

Our IoC container will instantiate a file validator and pass an appropriate implementation of ILogger into the constructor. Which implementation? That depends on how its implemented.

假设我在配置文件中给出了程序集和类的名称.语言需要将类的名称读取为字符串并使用反射来实例化它.

Let's say that I gave the name of the assembly and class in a configuration file. The language needs to read name of the class as a string and use reflection to instantiate it.

除非我们在编译时知道实现,否则没有类型安全的方法可以根据名称实例化一个类.

Unless we know the implementation at compile time, there is no type-safe way to instantiate a class based on its name.

后期绑定/鸭子打字

您想在运行时读取对象属性的原因有很多种.我会选择日志记录作为最简单的用例——假设您正在编写一个记录器,它接受任何对象并将其所有属性输出到一个文件中.

There are all kinds of reasons why you'd want to read the properties of an object at runtime. I'd pick logging as the simplest use case -- let say you were writing a logger which accepts any object and spits out all of its properties to a file.

public static void Log(string msg, object state) { ... }

可以覆盖所有可能的静态类型的 Log 方法,或者您可以只使用反射来读取属性.

You could override the Log method for all possible static types, or you could just use reflection to read the properties instead.

OCaml 和 Scala 等一些语言支持静态检查的鸭子类型(称为 结构类型),但有时您只是不了解对象接口的编译时知识.

Some languages like OCaml and Scala support statically-checked duck-typing (called structural typing), but sometimes you just don't have compile-time knowledge of an objects interface.

或者正如 Java 程序员所知,有时类型系统会按照您的方式进行,并要求您编写各种样板代码.有一篇著名的文章描述了有多少设计模式通过动态类型简化.

Or as Java programmers know, sometimes the type system will get your way and require you to write all kinds of boilerplate code. There's a well-known article which describes how many design patterns are simplified with dynamic typing.

偶尔绕过类型系统可以让您比静态类型更深入地重构代码,从而产生更清晰的代码(最好隐藏在程序员友好的 API 后面:)).许多现代静态语言都采用尽可能静态类型,必要时动态类型"的黄金法则,允许用户在静态和动态代码之间切换.

Occasionally circumventing the type system allows you to refactor your code down much further than is possible with static types, resulting in a little bit cleaner code (preferably hidden behind a programmer friendly API :) ). Many modern static languages are adopting the golden rule "static typing where possible, dynamic typing where necessary", allowing users to switch between static and dynamic code.

这篇关于反射如何不会导致代码异味?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆