展会信息港展会大全

C#反射 允许用户获得类型信息的C#特性
来源:互联网   发布日期:2016-01-26 10:17:06   浏览:2094次  

导读:其实说白了,反射就是能知道我们未知类型的类型信息这么一个东西.没什么神秘可讲!反射的核心是System Type。System Type包含了很多属性和方法,使用这些属性和方法可以在运行时得到类型信息反射(reflection) ...

其实说白了,反射就是能知道我们未知类型的类型信息这么一个东西.没什么神秘可讲!反射的核心是System.Type。System.Type包含了很多属性和方法,使用这些属性和方法可以在运行时得到类型信息

反射(reflection)是一种允许用户获得类型信息的C#特性。术语 反射 源自于它的工作方式:

Type对象映射它所代表的底层对象。对Type对象进行查询可以获得(反射)与类型相关的信息。反射是一种

功能强大的机制,它允许学习和使用只在运行时才能知道的类型功能。

这些是官方定义,其实说白了,反射就是能知道我们未知类型的类型信息这么一个东西.没什么神秘可讲!

反射的核心是System.Type。System.Type包含了很多属性和方法,使用这些属性和方法可以在运行时得到类型信息。

一旦得到类型信息,就可以调用其构造函数、方法和属性。因此,反射允许使用编译时不可用的代

码。因为反射涉及内容太多,我们主要学习常用的反射技术。

一、获取方法的相关信息

一旦有了Type对象,就可以使用GetMethods()方法来获取此类型支持的方法的列表。它的一种形式为:

MethodInfo[] GetMethods()

MethodInfo对象描述了主调类型所支持的方法,因此可以通过它的Name属性获得方法的名称。同时它还有两个重

要的方法,ReturnType和GetParameters()。

只读属性ReturnType为一个Type类型的对象,它为用户提供方法的返回类型信息。

GetParameters()返回一个方法的参数列表,它的基本形式为:

ParameterInfo[] GetParameters();

参数信息保存在ParameterInfo对象中。ParameterInfo类定义了大量描述参数信息的属性和方法。

其中常用属性是Name和ParameterType,这两个属性我就不介绍了,从字面上应该可以理解了。

好了,不说了,讲了这么多概念想必大家都不耐烦了。下面我们先看代码示例,估计你看了以后,再结合上面的

概念讲解,你一定会说:哦,原来如此!!!

复制代码 代码如下:

class MyClass

{

int x;

int y;

public MyClass(int i, int j)

{

x = i;

y = j;

}

public int Sum()

{

return x + y;

}

public bool IsBetween(int i)

{

if (x < i && i < y)

return true;

else

return false;

}

public void Set(int a, int b)

{

x = a;

y = b;

}

public void Set(double a, double b)

{

x = (int)a;

y = (int)b;

}

public void Show()

{

Console.WriteLine("x:{0},y:{1}", x, y);

}

}

运行结果:

请注意,除了MyClass定义的方法外,object定义的方法也会被显示。这是因为C#所有的类都继承于object。

另外,类型名称(如Int32)采用的是.net结构的名称。

二、 GetMethods()的另外一种形式

这种形式中可以指定各种标记,以此筛选出想要获取的方法,它的基本形式:

MethodInfo[] GetMethods(BindingFlags flags)

这种形式只获得与所指定的条件相匹配的方法,BindingFlages 是一个枚举,相关知识请参考:

msdn上 BindingFlags 枚举

可以使用OR运算符把两个或者更多的标记连接在一起。实际上,括号中至少应包含Instance(或Static)

与Public(或NotPublic)标记,否则将不会获得任何方法。

GetMethods()方法的BindingFlages形式的一个主要用途在于,它可以只获得某个类自身定义的方法而不

获得它从基类继承的方法,这对于object尤其有用。

例如用下列形式来替换前面程序中的GetMethods()语句:

复制代码 代码如下:

MethodInfo[] mi = t.GetMethods(BindingFlags.DeclaredOnly |

BindingFlags.Instance |

BindingFlags.Public);

进行上述更改后,程序的输出结果为:

可以看出,这里只显示了MyClass显示定义的方法。

使用反射调用方法:

一旦知道一个类型所支持的方法,就可以对方法进行调用。调用时,需使用包含在

MethodInfo中的Invoke()方法。调用形式:

object Invoke(object ob, object[] args)

这里ob是一个对象引用,将调用它所指向的对象上的方法。对于静态方法,ob必须为null。

所有需要传递给方法的参数都必须在args数组中指定。如果方法不需要参数,则args必须为null。

另外,数组args的元素数量参数必须等于参数的数量。Invoke()方法返回被调用方法的返回值。

要调用某个方法,只需在一个MethodInfo实例上调用Invoke(),该实例通过调用

GetMethods()

方法获得。请看事例:

复制代码 代码如下:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Reflection;

namespace Reflection

{

class Program

{

static void Main(string[] args)

{

// Demo1();

InvokeMethDemo();

Console.ReadKey();

}

static void InvokeMethDemo()

{

//获得MyClass的类型队形

Type t = typeof(MyClass);

MyClass reflectOb = new MyClass(10, 20);

Console.WriteLine("类名:{0}", t.Name);

Console.WriteLine("本类所提供的方法有:");

MethodInfo[] mi = t.GetMethods();

int val;

foreach (MethodInfo m in mi)

{

Console.WriteLine();

//显示参数

ParameterInfo[] pi = m.GetParameters();

if (m.Name == "Set" && pi[0].ParameterType == typeof(int))

{

Console.Write("Set(int,int)");

object[] args = new object[2];

args[0] = 9;

args[1] = 18;

m.Invoke(reflectOb, args);

}

else if (m.Name == "Set" && pi[0].ParameterType == typeof(double))

{

Console.Write("Set(double,double)");

object[] args = new object[2];

args[0] = 2.34;

args[1] = 13.56;

m.Invoke(reflectOb, args);

}

else if (m.Name.CompareTo("Sum") == 0) {

Console.Write("Sum() ");

val = (int)m.Invoke(reflectOb, null);

Console.WriteLine("Sum is {0}",val);

}

else if(m.Name.CompareTo("IsBetween")==0)

{

object[] args = new object[1];

args[0] = 17;

if ((bool)m.Invoke(reflectOb, args))

{

Console.WriteLine("{0}在x和y之间",args[0]);

}

}

Console.WriteLine();

}

}

}

}

class MyClass

{

int x;

int y;

public MyClass(int i, int j)

{

x = i;

y = j;

}

public int Sum()

{

return x + y;

}

public bool IsBetween(int i)

{

if (x < i && i < y)

return true;

else

return false;

}

public void Set(int a, int b)

{

x = a;

y = b;

Show();

}

public void Set(double a, double b)

{

x = (int)a;

y = (int)b;

Show();

}

public void Show()

{

Console.WriteLine("x:{0},y:{1}", x, y);

}

}

运行结果如下:

在前面例子中,由于MyClass类型的对象是显示创建的,因此使用反射技术来调用MyClass上的方法没有任何优势--以普通的方式调用对象上的方法会简单的多

但是,如果对象是在运行时动态创建的,反射的功能就显示出来了。在这种情况下,需要首先获取一个构造函数列表,然后再调用列表中的某个构造函数,创建一个该类型的实例。通过这种机制,可以在运行时实例化任意类型的对象而不必在声明中指定。

为了获得某个类型的构造函数,需要调用Type对象上的GetConstructors()。常用形式为:

ConstructorInfo[] GetConstructors()

该方法返回一个描述构造函数的ConstructorInfo对象数组。ConstructorInfo中常用的

是GetParamters()方法,该方法返回给定构造函数的参数列表。

一旦找到了合适的构造函数,就调用ConstructorInfo定义的Invoke()方法来创建对象:

object Invoke(object[] args)

需要传递给此方法的所有参数都在args中指定。如果不需要参数,args必须为null。另外,

args必须包含与参数个数相同的元素,并且实参的类型必须与形参的类型兼容。Invoke()方法返回

的是指向新构造对象的引用。

例子:

测试对象类

复制代码 代码如下:

class MyClass

{

int x;

int y;

public MyClass(int i)

{

Console.WriteLine("一个参数的构造函数:");

x = y = i;

}

public MyClass(int i, int j)

{

Console.WriteLine("两个参数构造函数:");

x = i;

y = j;

Show();

}

public int Sum()

{

return x + y;

}

public bool IsBetween(int i)

{

if (x < i && i < y)

return true;

else

return false;

}

public void Set(int a, int b)

{

Console.Write("函数:Set(int a, int b)");

x = a;

y = b;

Show();

}

public void Set(double a, double b)

{

Console.Write("函数:Set(double a, double b)");

x = (int)a;

y = (int)b;

Show();

}

public void Show()

{

Console.WriteLine("x:{0},y:{1}", x, y);

}

}

使用反射:

复制代码 代码如下:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Reflection;

namespace Reflection

{

class Program

{

static void Main(string[] args)

{

InvokeConsDemo();

Console.ReadKey();

}

static void InvokeConsDemo()

{

Type t = typeof(MyClass);

int val;

ConstructorInfo[] ci = t.GetConstructors();

Console.WriteLine("类构造函数如下:");

foreach (ConstructorInfo c in ci)

{

Console.Write("" + t.Name + "(");

ParameterInfo[] pi = c.GetParameters();

for (int i = 0; i < pi.Length; i++)

{

Console.Write(pi[i].ParameterType.Name + " " + pi[i].Name);

if (i + 1 < pi.Length) Console.Write(", ");

}

Console.WriteLine(") ");

}

Console.WriteLine();

int x;

for (x = 0; x < ci.Length; x++)

{

ParameterInfo[] pi = ci[x].GetParameters();

if (pi.Length == 2) break;

}

if (x == ci.Length)

{

Console.WriteLine("没有找到两个参数的构造函数"); return;

}

else

{

object[] consargs = new object[2];

consargs[0] = 10;

consargs[1] = 20;

object reflectOb = ci[x].Invoke(consargs);

Console.WriteLine("用reflectOb调用方法");

Console.WriteLine();

MethodInfo[] mi = t.GetMethods();

foreach (MethodInfo m in mi)

{

ParameterInfo[] pi = m.GetParameters();

if (m.Name.CompareTo("Set") == 0 && pi[0].ParameterType == typeof(int))

{

object[] args = new object[2];

args[0] = 12;

args[1] = 7;

m.Invoke(reflectOb, args);

}

else if (m.Name.CompareTo("Set") == 0 && pi[0].ParameterType == typeof(double))

{

object[] args = new object[2];

args[0] = 1.25;

args[1] = 7.5;

m.Invoke(reflectOb, args);

}

else if (m.Name.CompareTo("Sum") == 0)

{

val = (int)m.Invoke(reflectOb, null);

Console.WriteLine("Sum is {0}",val);

}

else if (m.Name.CompareTo("IsBetween") == 0)

{

object[] args = new object[1];

args[0] = 13;

if ((bool)m.Invoke(reflectOb, args))

{

Console.WriteLine("13 is between x and y");

}

}

else if (m.Name.CompareTo("Show") == 0)

{

m.Invoke(reflectOb, null);

}

}

}

}

}

}

运行结果为:

本例中,找到了一个两个参数的构造函数,那么使用下面的语句实例化了一个该类型的对象:

object reflectOb=ci[x].Invoke(consargs);

调用Invoke()方法后,reflectOb将引用一个MyClass类型的对象。此后,程序将执行

reflectOb上的方法。

注意:本例为了简单起见,假设了一个使用两个参数的构造函数,并且两个参数都为int类型。但在实际的应用程序中,必须检验每一个参数的类型。

赞助本站

人工智能实验室

相关热词: 反射 C

AiLab云推荐
展开

热门栏目HotCates

Copyright © 2010-2024 AiLab Team. 人工智能实验室 版权所有    关于我们 | 联系我们 | 广告服务 | 公司动态 | 免责声明 | 隐私条款 | 工作机会 | 展会港