反射基本类型
这里说反射基本类型,基本类型是针对泛型类型来说的,因为反射泛型会更加复杂一些。在前面的范例中,获得了程序集中的所有类型,并循环打印了它们,打印结果仅仅显示出了类型的全名,而通常需要关于类型更详细的信息,现在就来看看如何进一步查看类型信息。
1.获取基本信息
有了前面示例的介绍,下面的示例直接写出代码,如有必要,会在注释中加以说明。我们再写一个方法TypeExplore,用于获取类型的详细信息(记得AssemblyExplore只获取了类型的名称):
/// <summary>
/// 得到基本类型的信息
/// </summary>
/// <param name="t">Type类型</param>
public static void TypeExplore(Type t)
{
StringBuilder sb = new StringBuilder();
sb.Append("名称信息:\n");
sb.Append("Name: " + t.Name + "\n");
sb.Append("FullName: " + t.FullName + "\n");
sb.Append("Namespace: " + t.Namespace + "\n");
sb.Append("\n其他信息:\n");
sb.Append("BaseType(基类型): " + t.BaseType + "\n");
sb.Append("UnderlyingSystemType: " + t.UnderlyingSystemType + "\n");
sb.Append("\n类型信息:\n");
sb.Append("Attributes(TypeAttributes位标记): " + t.Attributes + "\n");
sb.Append("IsValueType(值类型): " + t.IsValueType + "\n");
sb.Append("IsEnum(枚举): " + t.IsEnum + "\n");
sb.Append("IsClass(类): " + t.IsClass + "\n");
sb.Append("IsArray(数组): " + t.IsArray + "\n");
sb.Append("IsInterface(接口): " + t.IsInterface + "\n");
sb.Append("IsPointer(指针): " + t.IsPointer + "\n");
sb.Append("IsSealed(密封): " + t.IsSealed + "\n");
sb.Append("IsPrimitive(基类型): " + t.IsPrimitive + "\n");
sb.Append("IsAbstract(抽象): " + t.IsAbstract + "\n");
sb.Append("IsPublic(公开): " + t.IsPublic + "\n");
sb.Append("IsNotPublic(不公开): " + t.IsNotPublic + "\n");
sb.Append("IsVisible: " + t.IsVisible + "\n");
sb.Append("IsByRef(由引用传递): " + t.IsByRef + "\n");
Console.WriteLine(sb.ToString());
}
然后在main函数中添加查看VS2008.Chapter11.AssemblySample.BaseClass基本数据类型的代码:
Assembly asm = Assembly.Load("AssemblySample");
Type t = asm.GetType("VS2008.Chapter11.AssemblySample.BaseClass");
TypeExplore(t);
运行程序,结果如下:
名称信息:
Name: BaseClass
FullName: VS2008.Chapter11.AssemblySample.BaseClass
Namespace: VS2008.Chapter11.AssemblySample
其他信息:
BaseType(基类型): System.Object
UnderlyingSystemType: VS2008.Chapter11.AssemblySample.BaseClass
类型信息:
Attributes(TypeAttributes位标记): AutoLayout, AnsiClass, Class, Public, Abstract
, BeforeFieldInit
IsValueType(值类型): False
IsEnum(枚举): False
IsClass(类): True
IsArray(数组): False
IsInterface(接口): False
IsPointer(指针): False
IsSealed(密封): False
IsPrimitive(基类型): False
IsAbstract(抽象): True
IsPublic(公开): True
IsNotPublic(不公开): False
IsVisible: True
IsByRef(由引用传递): False
值得注意的是Attributes属性,它返回一个TypeAttributes位标记,这个标记标识了类型的一些元信息,可以看到我们熟悉的Class、Public、Sealed。相应的,IsClass、IsSealed、IsPublic等属性也返回为True。
2.成员信息与MemberInfo类型
先考虑一下对于一个类型Type,可能会包含什么类型,常见的有字段、属性、方法、构造函数、接口、嵌套类型等。MemberInfo类代表着Type的成员类型,值得注意的是Type类本身又继承自MemberInfo类,理解起来并不困难,因为一个类型经常也是另一类型的成员。Type类提供 GetMembers()、GetMember()、FindMember()等方法用于获取某个成员类型。
在项目TestAssembly中,再添加一个方法MemberExplore(),来查看一个类型的所有成员类型。
代码如下:
/// <summary>
/// 得到一个类型的所有成员信息
/// </summary>
/// <param name="t">Type</param>
public static void MemberExplore(Type t)
{
StringBuilder sb = new StringBuilder();
MemberInfo[] memberInfo = t.GetMembers();
sb.Append("查看类型 " + t.Name + "的成员信息:\n");
foreach (MemberInfo mi in memberInfo)
{
sb.Append("成员:" + mi.ToString().PadRight(40) + " 类型: " + mi.MemberType + "\n");
}
Console.WriteLine(sb.ToString());
}
在main中添加调用代码
static void Main(string[] args)
{
AssemblyExplore();
Assembly asm = Assembly.Load("AssemblySample");
Type t = asm.GetType("VS2008.Chapter11.AssemblySample.BaseClass");
TypeExplore(t);
t = asm.GetType("VS2008.Chapter11.AssemblySample.DemoClass");
MemberExplore(t);
Console.ReadKey();
}
程序的运行结果是:
查看类型 DemoClass的成员信息:
成员:Void add_myEvent(VS2008.Chapter11.AssemblySample.DemoDelegate) 类型: Method
成员:Void remove_myEvent(VS2008.Chapter11.AssemblySample.DemoDelegate) 类型: Method
成员:Void set_Name(System.String) 类型: Method
成员:Void SayGreeting(System.String) 类型: Method
成员:System.String ToString() 类型: Method
成员:Boolean Equals(System.Object) 类型: Method
成员:Int32 GetHashCode() 类型: Method
成员:System.Type GetType() 类型: Method
成员:Void .ctor() 类型: Constructor
成员:System.String Name 类型: Property
成员:VS2008.Chapter11.AssemblySample.DemoDelegate myEvent 类型: Event
成员:System.String city 类型: Field
成员:System.String title 类型: Field
成员:System.String text 类型: Field
成员:VS2008.Chapter11.AssemblySample.DemoClass+NestedClass 类型: NestedType
使用了GetMembers()方法获取了成员信息的一个数组,然后遍历了数组,打印了成员的名称和类型。Name属性在编译后成为了get_Name()和set_Name()两个独立的方法;myEvent事件的注册(+=)和取消注册(-=)分别成为了add_myEvent()和remove_myEvent方法。同时,发现私有(private)字段name 没有被打印出来,另外,基类System.Object的成员GetType()和Equals()也被打印了出来。
有的时候,可能不希望查看基类的成员,也可能希望查看私有的成员,此时可以使用GetMembers()的重载方法,传入BindingFlags位标记参数来完成。BindingFlags位标记对如何获取成员的方式进行控制(也可以控制如何创建对象实例)。对于本例,如果想获取所有的公有、私有、静态、实例成员,那么只需要在方法MemberExplore中这样修改GetMembers()方法就可以了。
MemberInfo[] memberInfo = t.GetMembers(
BindingFlags.Public |
BindingFlags.Static |
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.DeclaredOnly
);
程序的运行结果是:
查看类型 DemoClass的成员信息:
成员:Void add_myEvent(VS2008.Chapter11.AssemblySample.DemoDelegate) 类型: Method
成员:Void remove_myEvent(VS2008.Chapter11.AssemblySample.DemoDelegate) 类型: Method
成员:System.String get_Name() 类型: Method
成员:Void set_Name(System.String) 类型: Method
成员:Void SayGreeting(System.String) 类型: Method
成员:Void .ctor() 类型: Constructor
成员:System.String Name 类型: Property
成员:VS2008.Chapter11.AssemblySample.DemoDelegate myEvent 类型: Event
成员:System.String name 类型: Field
成员:System.String city 类型: Field
成员:System.String title 类型: Field
成员:VS2008.Chapter11.AssemblySample.DemoDelegate myEvent 类型: Field
成员:System.String text 类型: Field
成员:VS2008.Chapter11.AssemblySample.DemoClass+NestedClass 类型: NestedType
可以看到,继承自基类System.Object的方法都被过滤掉了,同时,打印出了私有的name,myEvent 等字段。
现在如果想要获取所有的方法(Method),那么可以使用Type类的FindMembers()方法:
MemberInfo[] memberInfo = t.FindMembers(
MemberTypes.Method, // 说明查找的成员类型为 Method
BindingFlags.Public |
BindingFlags.Static |
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.DeclaredOnly,
Type.FilterName, "*"
);
Type.FilterName 返回一个MemberFilter类型的委托,它说明按照方法名称进行过滤,最后一个参数“*”,说明返回所有名称(如果使用“Get*”,则会返回所有以Get开头的方法)。现在的输出如下:
查看类型 DemoClass的成员信息:
成员:Void add_myEvent(VS2008.Chapter11.AssemblySample.DemoDelegate) 类型: Method
成员:Void remove_myEvent(VS2008.Chapter11.AssemblySample.DemoDelegate) 类型: Method
成员:System.String get_Name() 类型: Method
成员:Void set_Name(System.String) 类型: Method
成员:Void SayGreeting(System.String) 类型: Method
MemberInfo类有两个属性值得注意,一个是DeclaringType,一个是ReflectedType,返回的都是Type类型。DeclaredType返回的是该成员时所在的类型。比如说,回顾之前的一段代码:
MemberInfo[] members = typeof(DemoClass).GetMembers();
它将返回所有的公有成员,包括继承自基类的Equals()等方法,对于Equals()方法来说,它的DeclaringType 返回的是相当于typeof(Object)的类型实例,因为它是在 System.Object中被定义的;而它的ReflectedType 返回的则是相当于typeof(DemoClass)类型实例,因为它是通过DemoClass的类型实例被获取的。
3.字段信息 与 FieldInfo类型
如同之前所说,MemberInfo是一个基类,它包含的是类型的各种成员都公有的一组信息。实际上,对于字段、属性、方法、事件 等类型成员来说,它们包含的信息显然都是不一样的,所以,.Net中提供了 FiledInfo类型来封装字段的信息,它继承自MemberInfo。
如果希望获取一个类型的所有字段,可以使用GetFileds()方法。在项目TestAssembly中,再次添加一个方法FieldExplore()。
/// <summary>
/// 获得某个类型的字段信息
/// </summary>
/// <param name="t"></param>
public static void FieldExplore(Type t)
{
StringBuilder sb = new StringBuilder();
FieldInfo[] fields = t.GetFields();
sb.Append("查看类型 " + t.Name + "的字段信息:\n");
sb.Append(String.Empty.PadLeft(50, '-') + "\n");
foreach (FieldInfo fi in fields)
{
sb.Append("名称:" + fi.Name + "\n");
sb.Append("类型:" + fi.FieldType + "\n");
sb.Append("属性:" + fi.Attributes + "\n\n");
}
Console.WriteLine(sb.ToString());
}
在main函数中添加测试代码。程序的运行结果是:
查看类型 DemoClass的字段信息:
------------------------------------------
名称:city
类型:System.String
属性:Public
名称:title
类型:System.String
属性:Public, InitOnly
名称:text
类型:System.String
属性:Public, Static, Literal, HasDefault
值得一提的是fi.Attributes属性,它返回一个FieldAttributes位标记,这个位标记包含了字段的属性信息。对比之前定义的DemoClass类,可以看到,对于title字段,它的属性是public, InitOnly;对于Const类型的text字段,它的属性为Public,Static,Literal,HasDefault,由此也可以看出,声明一个const类型的变量,它默认就是静态static的,同时,由于给了它初始值,所以位标记中也包括HasDefault。
针对于FieldType位标记,FiledInfo类提供了一组返回为bool类型的属性,来说明字段的信息,常用的有:IsPublic, IsStatic, IsInitOnly, IsLiteral, IsPrivate 等。
如果想要获取私有字段信息,依然可以使用重载了的GetFields[]方法,传入BindingFlags参数,和上面的类似,这里就不重复了
4.属性信息与PropertyInfo类型
和字段类似,也可以通过 GetProperty()方法,获取类型的所有属性信息。
在项目TestAssembly中再添加一个方法PropertyExplore。
/// <summary>
/// 获得某个类型的属性信息
/// </summary>
/// <param name="t">Type</param>
public static void PropertyExplore(Type t)
{
StringBuilder sb = new StringBuilder();
sb.Append("查看类型 " + t.Name + "的属性信息:\n");
sb.Append(String.Empty.PadLeft(50, '-') + "\n");
PropertyInfo[] properties = t.GetProperties();
foreach (PropertyInfo pi in properties)
{
sb.Append("名称:" + pi.Name + "\n");
sb.Append("类型:" + pi.PropertyType + "\n");
sb.Append("可读:" + pi.CanRead + "\n");
sb.Append("可写:" + pi.CanWrite + "\n");
sb.Append("属性:" + pi.Attributes + "\n");
}
Console.WriteLine(sb.ToString());
}
程序的运行结果是:
查看类型 DemoClass的属性信息:
------------------------------
名称:Name
类型:System.String
可读:True
可写:True
属性:None
从前面的章节可以看到,Name属性会在编译后生成Get_Name()和Set_Name()两个方法,那么,应该可以利用反射获取这两个方法。PropertyInfo类的GetGetMethod()和GetSetMethod()可以完成这个工作,它返回一个MethodInfo对象,封装了关于方法的信息,这个会在后面看到。
5.方法信息与MethodInfo类型
与前面的类似,依然可以编写代码来查看类型的方法信息。
/// <summary>
/// 获得某个类型的方法信息
/// </summary>
/// <param name="t">Type</param>
public static void MethodExplore(Type t)
{
StringBuilder sb = new StringBuilder();
sb.Append("查看类型 " + t.Name + "的方法信息:\n");
sb.Append(String.Empty.PadLeft(50, '-') + "\n");
MethodInfo[] methods = t.GetMethods();
foreach (MethodInfo method in methods)
{
sb.Append("名称:" + method.Name + "\n");
sb.Append("签名:" + method.ToString() + "\n");
sb.Append("属性:" + method.Attributes + "\n");
sb.Append("返回值类型:" + method.ReturnType + "\n\n");
}
Console.WriteLine(sb.ToString());
}
程序的运行结果是:
查看类型 DemoClass的方法信息:
--------------------------------------------------
名称:add_myEvent
签名:Void add_myEvent(VS2008.Chapter11.AssemblySample.DemoDelegate)
属性:PrivateScope, Public, HideBySig, SpecialName
返回值类型:System.Void
名称:remove_myEvent
签名:Void remove_myEvent(VS2008.Chapter11.AssemblySample.DemoDelegate)
属性:PrivateScope, Public, HideBySig, SpecialName
返回值类型:System.Void
名称:set_Name
签名:Void set_Name(System.String)
属性:PrivateScope, Public, HideBySig, SpecialName
返回值类型:System.Void
名称:SayGreeting
签名:Void SayGreeting(System.String)
属性:PrivateScope, Public, Final, Virtual, HideBySig, VtableLayoutMask
返回值类型:System.Void
名称:ToString
签名:System.String ToString()
属性:PrivateScope, Public, Virtual, HideBySig, VtableLayoutMask
返回值类型:System.String
名称:Equals
签名:Boolean Equals(System.Object)
属性:PrivateScope, Public, Virtual, HideBySig, VtableLayoutMask
返回值类型:System.Boolean
名称:GetHashCode
签名:Int32 GetHashCode()
属性:PrivateScope, Public, Virtual, HideBySig, VtableLayoutMask
返回值类型:System.Int32
名称:GetType
签名:System.Type GetType()
属性:PrivateScope, Public, HideBySig
返回值类型:System.Type
与前面类似,MethodInfo 类也有一个Attributes属性,它返回一个MethodAttribute,MethodAttribute 位标记标明了方法的一些属性,常见的比如Abstract, Static, Virtual,Public, Private 等。
与前面不同的是,Method可以具有参数和返回值,MethodInfo类提供了GetParameters()方法获取 参数对象的数组,方法的参数都封装在了 ParameterInfo 类型中。查看ParameterInfo类型的方法与前面类似,这里就不再阐述了。
6. ConstructorInfo类型、EventInfo类型
从名称就可以看出来,这两个类型封装了类型的构造函数和事件信息,查看这些类型与之前的方法类似,这里就不再重复了。
11.2.4 动态创建对象
在前面节中,先了解了反射,然后利用反射查看了类型信息。前面学习的都是反射是什么,在接下来的章节中,将学习反射可以做什么。先看下如何动态地创建一个对象。
新建一个Console控制台项目,叫做CreateObjectByReflect。然后,添加一个MathClass类,本节中将通过对这个MathClass类的操作来进行说明。
类MathClass的代码
namespace CreateObjectByReflect
{
public class MathClass//要定义为public
{
//私有变量
private int x;
private int y;
//无参数的构造函数
public MathClass()
{
this.x = 0;
this.y = 0;
Console.WriteLine("我是无参数的构造函数,被调用!");
}
//带参数的构造函数
public MathClass(int x, int y)
{
this.x = x;
this.y = y;
Console.WriteLine("我是有参数的构造函数,被调用!");
}
}
}
1.动态的创建对象
动态的创建对象一般可以使用两种方法。一种是使用Assembly的CreateInstance方法。另一种方式是调用Activator类的静态方法CreateInstance。下面就分别看一下如何通过这两种方法分别采用无参数的构造函数与有参数的构造函数来创建对象。
1.1使用无参数构造函数创建对象
在项目CreateObjectByReflect中的main函数中添加下列代码:
namespace CreateObjectByReflect
{
class Program
{
static void Main(string[] args)
{
Assembly asm = Assembly.GetExecutingAssembly();
object obj = asm.CreateInstance("CreateObjectByReflect.MathClass", true);
if (null == obj)
{
Console.WriteLine("动态创建对象失败!");
}
Console.ReadKey();
}
}
}
运行程序:得到“我是无参数的构造函数,被调用!”说明对象创建成功,只是对象是一个object类型的,在以后的使用中需要强制类型转换。Assembly的CreateInstance有两个参数,第一个参数需要是一个字符串表示的完整的类名。第二个参数是一个bool类型,如果为true,表示对第一个参数不区分大小写。
下面使用另一种办法创建对象,同样在main函数中输入以下代码。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Runtime.Remoting;//使用Activator.CreateInstance的时候,需要添加
namespace CreateObjectByReflect
{
class Program
{
static void Main(string[] args)
{
ObjectHandle handler = Activator.CreateInstance(null, "CreateObjectByReflect.MathClass");
object obj = handler.Unwrap();
Console.ReadKey();
}
}
}
运行程序,对象被创建成功,输出“我是无参数的构造函数,被调用!”。
其中CreateInstance的第一个参数说明是程序集的名称,为null时表示当前程序集;第二个参数说明要创建的类型名称。Activator.CreateInstance返回的是一个ObjectHandle对象,必须进行一次Unwrap()才能返回Object类型,进而可以强制转换成我们需要的类型(本例中是MathClass)。ObjectHandle包含在System.Runtime.Remoting命名空间中,可见它是Remoting相关的,实际上ObjectHandle类只是一个对原类型进行了一个包装以便进行封送。
1.2使用有参数构造函数创建对象
如果想通过有参数的构造函数创建对象,可以使用Assembly的CreateInstance()的重载方法。在main函数只能够输入代码:
Assembly asm = Assembly.GetExecutingAssembly();
Object[] parameters = new Object[2]; // 定义构造函数需要的参数
parameters[0] = 3;
parameters[1] = 5;
Object obj = asm.CreateInstance("CreateObjectByReflect.MathClass", true, BindingFlags.Default, null, parameters, null, null);
重载方法有8个参数,前面的两个已经介绍过了,第三个参数BindingFlags在前面我们也用到过,它用于限定对类型成员的搜索。在这里指定Default,意思是不使用BingdingFlags的策略(你可以把它理解成null,但是BindingFlags是值类型,所以不可能为null,必须有一个默认值,而这个Default就是它的默认值);
接下来的参数是Binder,它封装了CreateInstance绑定对象(MathClass)的规则,几乎永远都会传递null进去,实际上使用的是预定义的DefaultBinder。
接下来是一个Object[]数组类型,它包含我们传递进去的参数,有参数的构造函数将会使用这些参数。接下来的参数是一个CultureInfo类型,它包含了关于语言和文化的信息。
2. 动态调用方法
接下来看一下如何动态地调用方法。注意,本节讨论的调用不是将上面动态创建好的对象由Object类型转换成MathClass类型再进行方法调用,这和“常规调用”就没有区别了,让我们以.Net Reflection的方式来进行方法的调用。继续进行之前,我们为MathClass添加两个方法,一个实例方法,一个静态方法。
//实例方法
public int Add()
{
int total = 0;
total = x + y;
Console.WriteLine("实例方法被引发: ");
Console.WriteLine(String.Format("[Add]: {0} + {1} = {2}", x, y, total));
return total;
}
//静态方法
public static void Add(int x, int y)
{
int total = x + y;
Console.WriteLine("静态方法被引发: ");
Console.WriteLine(String.Format("[Add]: {0} +{1} = {2}", x, y, total));
}
调用方法的方式一般有两种:
在类型的Type对象上调用InvokeMember()方法,传递想要在其上调用方法的对象(也就是刚才动态创建的MathClass类型实例),并指定BindingFlags为InvokeMethod。根据方法签名,可能还需要传递参数。
先通过Type对象的GetMethond()方法,获取想要调用的方法对象,也就是MethodInfo对象,然后在该对象上调用Invoke方法。根据方法签名,可能还需要传递参数。
需要说明的是,使用InvokeMember不限于调用对象的方法,也可以用于获取对象的字段、属性,方式都是类似的,本文只说明最常见的调用方法。
1.使用InvokeMember调用方法
先看第一种方法,代码很简单,只需要两行(注意obj在上节已经创建,是MathClass类型的实例):
Type t = typeof(MathClass);
int result = (int)t.InvokeMember("Add", BindingFlags.InvokeMethod, null, obj, null);
Console.WriteLine(String.Format("结果是{0}", result));
输出:
实例方法被引发:
[Add]: 3 + 5 = 8
The result is 8
在InvokeMember方法中,第一个参数说明了想要调用的方法名称;第二个参数说明是调用方法(因为InvokeMember的功能非常强大,不光是可以调用方法,还可以获取/设置 属性、字段等。);第三个参数是Binder,null说明使用默认的Binder;第四个参数说明是在这个对象上(obj是MathClass类型的实例)进行调用;最后一个参数是数组类型,表示的是方法所接受的参数。
再看一下对于静态方法应该如何调用:
Object[] parameters2 = new Object[2];
parameters2[0] = 6;
parameters2[1] = 9;
t.InvokeMember("Add", BindingFlags.InvokeMethod, null, typeof(MathClass), parameters2);
输出:
静态方法被引发:
[Add]: 6 +9 = 15
和上面对比一下:首先,第四个参数传递的是 typeof(MathClass),不再是一个MathClass实例类型,这很容易理解,因为我们调用的是一个静态方法,它不是基于某个具体的类型实例的,而是基于类型本身;其次,因为静态方法需要提供两个参数,所以我们以数组的形式将这两个参数进行了传递。
2.使用MethodInfo.Invoke调用方法
再看下第二种方式,先获得一个MethodInfo实例,然后调用这个实例的Invoke方法,我们看下具体如何做:
Type t1 = typeof(MathClass);
MethodInfo mi = t.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public);
mi.Invoke(obj, null);
输出:
实例方法被引发:
[Add]: 3 + 5 = 8
在代码的第二行,先使用GetMethod方法获取了一个方法对象MethodInfo,指定BindingFlags为Instance和Public,因为有两个方法都命名为“Add”,所以在这里指定搜索条件是必须的。接着使用Invoke()调用了Add方法,第一个参数obj是前面创建的MathClass类型实例,表明在该实例上创建方法;第二个参数为null,说明方法不需要提供参数。
再看下如何使用这种方式调用静态方法:
Type t2 = typeof(MathClass);
Object[] par2 = new Object[2];
par2[0] = 6;
par2[1] = 9;
MethodInfo mi1 = t.GetMethod("Add", BindingFlags.Static | BindingFlags.Public);
Mi1.Invoke(null, par2);
输出:
静态方法被引发:
[Add]: 6 +9 = 15
可以看到与上面的大同小异,在GetMethod()方法中,指定为搜索BindingFlags.Static,而不是BindingFlags.Public,因为要调用的是静态的Add方法。在Invoke()方法中,需要注意的是第一个参数,不能在传递MathClass类型实例,而应该传递MathClass的Type类型或者直接传递null。因为静态方法不是属于某个实例的。
通过上面的例子可以看出:使用反射可以达到最大程度上的多态,举个例子,你可以在页面上放置一个DropDownList控件,然后指定它的Items的value为某个类的方法的名称,然后在SelectedIndexChanged事件中,利用value的值来调用类的方法。而在以前,你只能写一些if else 语句,先判断DropDownList返回的值,根据值再决定调用哪个方法。使用这种方式,编译器在代码运行之前(或者说用户选择了某个选项之前)完全不知道哪个方法将被调用,这也就是常说的 迟绑定(Late Binding)。
联系客服