Welcome

首页 / 软件开发 / C# / Effective C#原则43:请勿滥用反射

Effective C#原则43:请勿滥用反射2010-12-13 博客园 Wu.Country@侠缘译创建二进制的组件时,同时也意味着你要使用迟后绑定和反射来查找你所须 要的具有特殊功能代码。反射是一个很有力的工具,而且它让你可以写出可动态 配置的软件。使用反射,一个应用程序可以通过添加新的组件来更新功能,而这 些组件是在软件最开始发布时没有的。这是有利的。

这一伸缩性也带来 了一些复杂的问题,而且复杂问题的增加又会增加出现其它问题的可能。当你使 用反射时,你是围绕着C#的安全类型。然而,成员调用的参数和返回值是以 System.Object类型存在的。你必须在运行时确保这些类型是正确的。简单的说 ,使用反射可以让创建动态的程序变得很容易,但同时也让程序出现错误变得很 容易。通常,简单的思考一下,你就可以通过创建一系列接口集合来最小化或者 移除反射,而这些接口集合应该表达你对类型的假设。

反射给了你创建 类型实例的功能,以及在对象上调用成员方法,以及访问对象上的成员数据。这 听上去就跟每天的编程任务是一样的。确实是这样的,对于反射,并没有什么新 奇的:它就是动态创建其它的二进制组件。大多数情况下,你并不须要像反射这 样的伸缩功能,因为有其它可选的更易维护的方案。

让我们从创建一个 给定类型的实例开始,你可以经常使用一个类厂来完成同样的任务。考虑下面的 代码,它通过使用反射,调用默认的构造函数创建了一个MyType 的实例:

// Usage:Create a new object using reflection:
Type t = typeof( MyType );
MyType obj = NewInstance( t ) as MyType;
// Example factory function, based on Reflection:
object NewInstance( Type t )
{
// Find the default constructor:
ConstructorInfo ci = t.GetConstructor( new Type[ 0 ] );
if ( ci != null )
// Invoke default constructor, and return
// the new object.
return ci.Invoke( null );
// If it failed, return null.
return null;
}

代码通过反射检测了类型,而且调用了默认的构造函数来创建了 一个对象。如果你须要在运行时创建一个预先不知道任何信息的类型实例,这是 唯一的选择。这是一段脆弱的代码,它依懒于默认的构造函数的存在。而且在你 移除了MyType类型的默认构造函数时仍然是可以通过编译的。你必须在运行时完 成检测,而且捕获任何可能出现的异常。一个完成同样功能的类厂函数,在构造 函数被移除时是不能通过编译的:

public MyType NewInstance( )
{
return new MyType();
}

(译注:其实 VS.Net会给我们添加默认的构造函数,所以上面的两个方法都是可以编译,而且 可以正确运行的。本人做过测试。但如果给构造函数添加访问限制,那么可以让 类厂无法构造对象而产生编译时错误。)

你应该使用静态的类厂函数来取 代依懒于反射的实例创建方法。如果你须要实例对象使用迟后数据绑定,那么应 该使用类厂函数,而且使用相关的特性来标记它们(参见原则42)。

另一 个反射的潜在的用处就是访问类型的成员。你可以使用成员名和类型在运行时来 调用实际的函数:

// Example usage:
Dispatcher.InvokeMethod( AnObject, "MyHelperFunc" );
// Dispatcher Invoke Method:
public void InvokeMethod ( object o, string name )
{
// Find the member functions with that name.
MemberInfo[] myMembers = o.GetType( ).GetMember( name );
foreach( MethodInfo m in myMembers )
{
// Make sure the parameter list matches:
if ( m.GetParameters( ).Length == 0 )
// Invoke:
m.Invoke( o, null );
}
}