首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅谈.Net反射 10

浅谈.Net反射 10

作者头像
小蜜蜂
发布2019-07-30 11:25:05
4240
发布2019-07-30 11:25:05
举报
文章被收录于专栏:明丰随笔明丰随笔明丰随笔

在前面几篇当中,先了解了反射,然后利用反射查看了类型信息,并学习了如何创建自定义特性,以及如何利用反射来获取特性对象。

在本文中,将学习如何使用反射动态地创建一个对象。

动态创建对象

新建一个类Calculator作为示范,其代码如下:

public class Calculator
{
  private int x;
  private int y;
  public Calculator()
  {
    x = 0;
    y = 0;
    Console.WriteLine("Calculator() invoked");
  }
  public Calculator(int x, int y)
  {
    this.x = x;
    this.y = y;
    Console.WriteLine("Calculator(int x, int y) invoked");
  }
}

创建对象通常有两种方式

1. Assembly的CreateInstance方法

2. Activator的CreateInstance方法

下面是它们的方法签名,有多个重载。

Assembly的CreateInstance方法:

public object CreateInstance(string typeName);
public object CreateInstance(string typeName, bool ignoreCase);
public virtual object CreateInstance(string typeName, bool ignoreCase, BindingFlags bindingAttr, Binder binder, object[] args, CultureInfo culture, object[] activationAttributes);

Activator的CreateInstance方法:

public static object CreateInstance(Type type);
public static object CreateInstance(Type type, bool nonPublic);
public static object CreateInstance(Type type, params object[] args);
public static object CreateInstance(Type type, object[] args, object[] activationAttributes);
public static object CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, object[] args, CultureInfo culture, object[] activationAttributes);
public static T CreateInstance<T>();
public static ObjectHandle CreateInstance(string assemblyName, string typeName);
public static ObjectHandle CreateInstance(string assemblyName, string typeName, object[] activationAttributes);
public static ObjectHandle CreateInstanceFrom(AppDomain domain, string assemblyFile, string typeName);

对于上面方法有些返回object,有一些返回ObjectHandle,实际上ObjectHandle类只是一个对原类型进行了一个包装以便进行封送。一个ObjectHandle对象,必须执行一次Unwrap()才能返回Object类型。

使用无参数构造函数创建对象

1. Assembly的CreateInstance方法

Assembly asm = Assembly.GetExecutingAssembly();
Object obj = asm.CreateInstance("ReflectionDemo.Calculator", true);
// 输出:Calculator() invoked

2. Activator的CreateInstance方法

ObjectHandle handler = Activator.CreateInstance("ReflectionDemo", "ReflectionDemo.Calculator");
Object obj2 = handler.Unwrap();

使用有参数构造函数创建对象

Assembly asm = Assembly.GetExecutingAssembly();
Object[] parameters = new Object[2]; // 定义构造函数需要的参数
parameters[0] = 3;
parameters[1] = 5;
Object obj3 = asm.CreateInstance("ReflectionDemo.Calculator" //类名称
  , true //类名称忽略大小写
  , BindingFlags.Default 
  , null
  , parameters //构造器参数
  , null
  , null);

看一下CreateInstance需要提供的参数:

1)前两个参数在上一小节已经说明过了;

2)BindingFlags在前面也出现过,它用于限定对类型成员的搜索。在这里指定Default,意思是不使用BingdingFlags的策略;

3)接下来的参数是Binder,它封装了CreateInstance绑定对象的规则,几乎永远都会传递null进去,使用内置的DefaultBinder;

4)接下来是一个Object[]数组类型,它包含传递进去的构造函数参数,有参数的构造函数将会使用这些参数。Object[]数组中元素的顺序,与构造函数参数列表中的顺序需要保持一致。

5)接下来的参数是一个CultureInfo类型,它包含了关于语言和文化的信息(简单理解就是对于ToString("c"),什么时候显示“¥”,什么时候显示“$”)。

动态调用方法

接下来再看一下如何动态地调用方法。本小节讨论的调用不是将上面动态创建好的对象由Object类型强制转换成Calculator类型再进行方法调用,这样就和普通的方法调用没有区别了,而是利用反射,基于字符串来调用方法。之所以称为“动态”,是因为字符串可以由各种途径获得,比如是从客户端传递进来的一个参数。

先为Calculator添加一个实例方法,一个静态方法作为本节的示例:

public int Add()
{
  int total = 0;
  total = x + y;
  Console.WriteLine("Invoke Instance Method: ");
  Console.WriteLine(String.Format("[Add]: {0} plus {1} equals to {2}", x, y, total));
  return total;
}

public static void Add(int x, int y)
{
  int total = x + y;
  Console.WriteLine("Invoke Static Method: ");
  Console.WriteLine(String.Format("[Add]: {0} plus {1} equals to {2}", x, y, total));
}

调用方法的方式一般有两种:

1. 通过Type对象的GetMethond()方法,获取想要调用的方法对象,也就是MethodInfo对象,然后在该对象上调用Invoke方法。根据方法签名,可能还需要传递参数。

2. 在类型的Type对象上调用InvokeMember()方法,传递要在其上调用方法的对象(对本例而言,就是Calculator类型实例),并指定BindingFlags为InvokeMethod。根据方法签名,可能还需要传递参数。

需要说明的是,使用InvokeMember()不限于调用对象的方法,也可以用于获取对象的字段、属性,实现方式都是类似的,本节只说明最常见的调用方法。

通过Type对象的GetMethond()方法,得到MethodInfo对象,使用MethodInfo对象进行方法调用。

调用一个实例方法,代码示例:

Type t = typeof(Calculator);
Calculator c = new Calculator(3, 5);
MethodInfo mi = t.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public);
mi.Invoke(c, null);

输出为:

Calculator(int x, int y) invoked

Invoke Instance Method:

[Add]: 3 plus 5 equals to 8

先使用GetMethod()方法获取了一个方法对象MethodInfo,指定BindingFlags为Instance和Public,因为Calculator类中有两个方法都命名为"Add",所以在这里指定搜索条件是必需的。

接着使用Invoke()调用了Add方法,第一个参数是Calculator的类型实例,表明在该实例上调用方法;第二个参数为null,说明方法不需要提供参数。

调用一个静态方法,代码示例:

Type t = typeof(Calculator);
object[] parameters = new object[] { 6, 9 };
MethodInfo mi = t.GetMethod("Add", BindingFlags.Static | BindingFlags.Public);
mi.Invoke(null, parameters);

输出为:

Invoke Static Method:

[Add]: 6 plus 9 equals to 15

可以看到与调用实例方法大同小异,在GetMethod()方法中,指定为搜索BindingFlags.Static,而不是BindingFlags.Public,因为现在要调用的是静态的Add()方法。

在Invoke()方法中,第一个参数不需要再传递Calculator的类型实例,因为静态方法不是属于某个具体实例的。

使用InvokeMember调用方法

Type t = typeof(Calculator);
Calculator c = new Calculator(3, 5);
int result = (int)t.InvokeMember("Add", BindingFlags.InvokeMethod, null, c, null);
Console.WriteLine(String.Format("The result is {0}", result));

输出的结果为:

Calculator(int x, int y) invoked

Invoke Instance Method:

[Add]: 3 plus 5 equals to 8

The result is 8

在InvokeMember()方法中,

第一个参数是想要调用的方法名称;

第二个参数是调用方法(InvokeMember()方法的功能非常强大,不光可以调用方法,还可以获取/设置属性、字段等。);

第三个参数是Binder,null说明使用默认的System.Type.DefaultBinder;

第四个参数是在哪个类型实例上进行调用,对于本例来说,是c,这是因为调用的是一个实例方法;

最后一个参数是数组类型,表示的是方法所接受的参数。

再看一下对于静态方法应该如何调用:

object[] parameters = { 6, 9};
Type t = typeof(Calculator);
t.InvokeMember("Add", BindingFlags.InvokeMethod, null, t, parameters);

输出的结果为:

Invoke Static Method:

[Add]: 6 plus 9 equals to 15

和调用实例方法进行一下对比:

首先,第四个参数传递的是typeof(Calculator),不再是一个Calculator实例类型,这是因为调用的是一个静态方法,它不是基于某个具体的类型实例,而是基于类型本身;

其次,因为Add()静态方法需要提供两个参数,所以以数组的形式将这两个参数进行了传递。

通过上面的例子可以看出:使用反射可以在最大程度上实现多态。举个例子,可以在页面上放置一个下拉框,然后指定它的列表项的Value为某个类型的方法名称,使用Value的值来调用该类型的方法。如果不使用反射,则只能写一些if-else语句,先判断选择的项的值,然后再根据值决定调用哪个方法。当使用这种方式时,编译器在代码运行之前(或者说用户选择了某个列表项之前),完全不知道类型的哪个方法将被调用,这也就是常说的迟绑定(Late Binding)。

本文回顾:

动态创建对象

动态调用方法

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 明丰随笔 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档