如何调用方法?

本示例阐释如何通过反射调用各种方法。由于所调用方法的名称存储在字符串中,因此该机制提供在运行时(而不是在设计时)指定要调用的方法的功能,提供了使您的用户可以控制调用哪个特定方法的余地。尽管本演示集中于调用方法,如果需要您还可以设置和获取属性和字段。有关本主题的另一个实例示教,请参阅如何使用数学函数主题下的示例。

 
C# ListMembers.aspx

[运行示例] | [查看源代码]

在许多代码方案中,在执行任务以前您知道要实现的任务。因此,您可以指定需要调用的方法以及需要传递给它们的参数。但是,还有一些情况下您可能希望根据特定方案或用户操作动态调用方法。该功能可通过 Reflection 命名空间使用,方法是使用 Type 对象上的 InvokeMember 方法。

您还可以进行其他操作,如获取或设置指定属性的值。这些操作可通过 BindingFlags 枚举使用。InvokeMethod 的第二个参数是您指定的 BindingFlags 操作的组合。例如,如果想调用某个类上的静态方法,可以在 BindingFlagsInvokeMethod BindingFlag 中包括该静态元素。下面的示例展示如何调用名为 SayHello 的假想方法,其中 SayHello 是静态方法。

// calling a static method, receiving no arguments

// don't forget that we are using object in the reflection namespace...
using System;
using System.Reflection;

public class Invoke {

	public static void Main (String [] cmdargs) {

		// Declare a type object, used to call our InvokeMember method...
		Type t = typeof (TestClass);

		// BindingFlags has three bitor'ed elements. Default indicates
		// that default binding rules should be applied.
		t.InvokeMember ("SayHello",
				BindingFlags.Default | BindingFlags.InvokeMethod
				| BindingFlags.Static, null,
				null, new object [] {});
	}
}
C# VB  

快速查看一下传递给 Invoke 方法的其余参数。传递的第一个空参数请求使用默认联编程序绑定正在调用的方法。当调用默认联编程序时,请包含默认的 BindingFlags。第三个参数可以不为空,您可以指定一个 Binder 对象,它定义一组属性并启用绑定,这可能涉及选择重载方法或强制参数类型。第二个空参数是您在其上调用所选方法的对象。最后,传递由成员接收的参数对象数组。在本例中,SayHello 方法不接收任何参数,因此传递一个空数组。

下面的情况略有不同。调用名为 ComputeSum 的另一个静态方法,但是在此情况下,此方法需要两个参数。因此,用这些参数填充一个对象数组,并将它们作为最后一个参数传递到 InvokeMember 中。

// Calling a static method, which needs arguments
object [] args = new object [] {100.09, 184.45};


// we know that this particular method returns a value, being the computed sum,
// so we create a variable to hold the return
// note the datatype of the return is object, the only datatype InvokeMethod returns...
object result;

// invoke the method. Note the change in the last parameter: the array we populated...
result = t.InvokeMember ("ComputeSum", BindingFlags.Default | _
		BindingFlags.InvokeMethod | BindingFlags.Static,
		null, null, args);

// write the results to the user's console...
Console.WriteLine ("{0} + {1} = {2}", args[0], args[1], result);
C# VB  

在前两个示例中,已调用了静态方法。还可以调用实例方法。若要这样做,将您要在其上调用方法的类型的对象作为第三个参数传递。本示例还展示为了使用 InvokeMember,您不必有实际的 Type 对象。在此情况下,通常将希望使用所拥有的类实例来调用 GetType,如下面的示例所示。注意由于未调用静态方法,所以 BindingFlags 已更改。

// Calling  an instance method
// we need an object reference to invoke an instance member
TestClass c = new TestClass ();


// use the instance of our class to call GetType
// we no longer include the Static element in BindingFlags for our |
// the fourth parameter is no longer null: we instead pass an instance
// of the object we wish to invoke our method on
c.GetType().InvokeMember ("AddUp", BindingFlags.Default | BindingFlags.InvokeMethod,
			null, c, new object [] {});
c.GetType().InvokeMember ("AddUp", BindingFlags.Default | BindingFlags.InvokeMethod,
			null, c, new object [] {});
C# VB  

有时不想调用方法,而需要调用其他成员,如属性或字段。若要实现它,只需更改 BindingFlags 组合(而不是 InvokeMethod)以包含适当元素即可。下面的示例展示获取和设置字段值。所讨论字段不是静态字段,因此需要创建一个对象实例来请求该字段。设置字段值时,需要将所设置的值作为对象数组参数的唯一元素传递。获取值时,需要将 InvokeMember 方法的返回类型分配给一个对象。

// Setting a field. Assume we are using the same Type and Class declared in the
// previous examples (t and c). The field we are setting is the Name field
// note the BindingFlags argument now includes SetField rather thanInvokeMember
// Further, this is an instance field, so we pass the instance of our class
t.InvokeMember ("Name", BindingFlags.Default | BindingFlags.SetField,
		null, c, new object [] {"NewName"});

// similar usage...
result = t.InvokeMember ("Name", BindingFlags.Default | BindingFlags.GetField,
		null, c, new object [] {});

Console.WriteLine ("Name == {0}", result);
C# VB  

还可以获取和设置属性,但在本示例中,假定所设置属性是一个具有多个元素的数组或集合。若要指定特定元素的设置,您需要指定索引。若要设置属性,请分配 BindingFlags.SetProperty。若要指定属性的集合索引或数组索引,请将要设置元素的索引值放在对象数组的第一个元素中,然后将要设置的值作为第二个元素。若要取回该属性,请将索引作为对象数组中的唯一元素传递,指定 BindingFlags.GetProperty。

// Set an indexed property value
int index = 3;

// specify BindingFlags.SetProperty, and because this is an instance property,
// pass the object to call the property on (c). In the object array, make two elements,
// the first being the index, and the second being the value to set
t.InvokeMember ("Item", BindingFlags.Default |BindingFlags.SetProperty,
			null, c, new object [] {index, "NewValue"});

// Get an indexed property value
// specify BindingFlags.GetProperty, and because this is an instance property,
// pass the object to call the property on (c). In the object array, specify the index only
result = t.InvokeMember ("Item", BindingFlags.Default |BindingFlags.GetProperty,
			null, c, new object [] {index});

Console.WriteLine ("Item[{0}] == {1}", index, result);
C# VB  

还可以使用命名参数,在此情况下需要使用 InvokeMember 方法的另一个重载版本。像迄今一直进行的那样创建对象参数的数组,并创建所传递参数的名称的字符串数组。您要使用的重载方法接受参数名列表作为最后一个参数,并接受要设置的值的列表作为第五个参数。在本演示中,所有其他参数都可以为空(当然前两个除外)。

// Calling a method using named arguments

// the argument array, and the parameter name array. Obviously, you will need
// to determine the names of the parameters in advance
object[] argValues = new object [] {"Mouse", "Micky"};
String [] argNames = new String [] {"lastName", "firstName"};

// the first five parameters for this overloaded method are the same as the
// the five parameters we have used to this point. The final parameter needs to be
// set to the names of the parameters
t.InvokeMember ("PrintName", BindingFlags.Default | BindingFlags.InvokeMethod,
			null, null, argValues, null, null, argNames);
C# VB  

下一个示例展示如何调用类上的默认成员。确保在其上进行调用的类指定有默认成员。然后在 InvokeMember 方法中,不要指定要调用成员的名称,如本示例所示。

// our class with it's default member specified, using the defaultmemeber attribute
[DefaultMemberAttribute ("PrintTime")]
public class TestClass2 {

	public void PrintTime () {
		Console.WriteLine (DateTime.Now);
	}
}

// the client code that uses the above class...


Type t3 = typeof (TestClass2);
t3.InvokeMember ("", BindingFlags.Default |BindingFlags.InvokeMethod,
			null, new TestClass2(), new object [] {});
C# VB  

最后一个示例使用略有不同的过程调用方法。不直接使用 Type 对象,而是直接创建一个单独的 MethodInfo 对象来表示将调用的方法。然后调用 MethodInfo 对象上的 Invoke 方法,传递需要在其上调用方法的对象的实例(在要调用实例方法的情况下,但是,如果方法是静态的,则为空)。像以前一样,需要参数的对象数组。如果需要,该特定示例允许您通过引用传递参数。

// Invoking a ByRef member
MethodInfo m = t.GetMethod("Swap");


args = new object[2];
args[0] = 1;
args[1] = 2;

m.Invoke(new TestClass(),args);

Console.WriteLine ("{0}, {1}", args[0], args[1]);
C# VB  

posted @ 2005-03-11 17:32 HAL9000 阅读(1677) | 评论 (0)编辑

如何列出某类型的所有成员

本示例使您可以列出给定数据类型的成员。列出类型成员的功能是快速发现哪些元素可用的很好方式。它是在系统中进行报告以及帮助开发用户文档的重要工具。使用 Reflection 命名空间,您可以控制希望显示给用户的成员类型以及其他信息(如特定方法的可见性)。还可以获取类中所有成员的信息,或仅指定某些子集(如方法或字段)。

 
C# ListMembers.aspx

[运行示例] | [查看源代码]

您可能想知道为何获取特定类型的信息很重要。毕竟,这就是帮助系统和帮助文档的用途,不是吗?下面的示例可以帮助您创建用户文档,或用于帮助动态调用方法或设置属性。

需要执行以下几个步骤。首先,需要获取用户希望使用的类型(以字符串的形式)。确定了要使用的类型后,需要分配一个对象来表示该类型。这将进行两项工作:创建后面步骤可以使用的对象,还确保指定类型存在并且可被系统找到。下面的示例向 System.String 类型分配一个对象。注意,尽管此处示例中通过“控制台”(Console) 对象向用户提供反馈,实际示例却将反馈发送给一个 ASP.NET 标签对象。但解释相同。

// don't forget your using statements at the top of your code...
Using System;
Using System.Reflection;

// class declaration, and method declaration...

// remember that this string is case-sensitive, so be careful
Type t = Type.GetType("System.String");

// check to see if we have a valid value. If our object is null, the type does not exist...
if (t == null) {
	// Don't assume that it is a SYSTEM datatype...
	Console.WriteLine("Please ensure you specify only valid types in the type field.");
	Console.WriteLine("REMEMBER: The Case matters (Byte is not the same as byte).");

	return; // don't continue processing
}
C# VB  

有了有效的类型对象以后,下一个问题是希望为类型检索什么类型的成员?是需要方法、静态方法还是实例字段?在 Reflection 命名空间中,有一组 Info 对象,每个对象表示您系统的一组不同成员。例如,有一个 MethodInfo 对象可表示有关某方法的信息。还有一个一般 MemberInfo 对象,它表示给定类中可以存在的所有成员。

使用该信息,您可设置以下数组来查看刚刚创建的类型,并弄清类型中有哪种类型的信息。下面示例中的“位”运算符(|符号,或 Visual Basic 中的 BitOr)请求满足指定约束的类型的所有信息。该示例展示如何获取所有字段和所有方法。

// declare and populate the arrays to hold the information...
FieldInfo [] fi = t.GetFields (BindingFlags.Static |
		BindingFlags.NonPublic | BindingFlags.Public);     // fields

MethodInfo [] mi = t.GetMethods (BindingFlags.Static |
		BindingFlags.NonPublic | BindingFlags.Public);     // methods
C# VB  

下一步是迭代通过每个数组,并在屏幕上列出数组中的元素(显然,您将实际处理这些元素或标识数组中的某个特定元素)。有多种方法可以进行该操作,但在该示例中使用 Foreach(Visual Basic 中为 For Each)语句。



// iterate through all the method members
foreach (MethodInfo m in mi) {
	Console.WriteLine(m);
}

// iterate through all the field members
foreach (FieldInfo f in fi) {
	Console.WriteLine(f);
}

// etc.... for each array type
C# VB  

前面的代码工作良好,但请注意两条 Foreach(Visual Basic 中为 For Each)语句多么类似。将所有这些都写出来非常费力而且杂乱(并且如果以后更改代码,可能需要大量维护)。可以通过返回到 MemberInfo 对象来规避这一点。MemberInfo 对象包括所有可能的信息集(方法、字段和接口等等)。这可以对我们有所帮助,因为我们可以将以前的多个 Foreach 语句写成一个语句,传入数组以进行分析。

// call the routine below, passing the relevant array we made in the previous step
PrintMembers( mi ); // the method information
PrintMembers( fi ); // the field information

void PrintMembers (MemberInfo [] ms ) {



	// MemberInfo is the generic info object. This can be any of the other info objects.
	foreach (MemberInfo m in ms) {
		Console.WriteLine(m);
	}
}
C# VB  

运行该示例时您将注意到,同时会发生其他一些事情。不必硬编码 System.String 对象,可以指定要获取有关哪个类的信息。它还使您可以控制是要显示静态信息还是实例信息。

posted @ 2005-03-11 17:31 HAL9000 阅读(1016) | 评论 (0)编辑

如何获取程序集内的类型

本示例阐释如何检索给定程序集的所有类型。若要浏览程序集的类型,首先需要标识想操作的程序集。在使某对象引用了感兴趣的程序集后,可以在该程序集上调用 GetTypes 方法,它返回包含该程序集内所有类型的一个数组。您可以使用控制逻辑标识该数组中的更具体类型,并使用迭代逻辑分析您的数组,在需要时向用户返回类型信息。检索类型信息的功能对确定可用于给定任务的其他类型很有用,或对标识可为您提供所需功能的现有元素很有用。

 
C# GetTypes.aspx

[运行示例] | [查看源代码]

从特定程序集检索类型时要学习的首要内容是如何标识程序集。本"快速入门"展示检索程序集的两种方法。第一种方法是标识要在程序集内查找的特定对象,并向程序集请求该对象的模块(记住模块是类型和代码的逻辑分组,如 .dll 或 .exe)。第二种方法是使用 Assembly 类的 LoadFrom 方法,为指定模块(如 myapp.exe)加载特定程序集。

// don't forget your using statements
using System;
using System.Reflection;
// ...

// Getting an Assembly, method 1. Get the mscorlib assembly
// Note that other types such as String, or Int32 would have worked just as well,
// since they reside in the same assembly
Assembly a = typeof(Object).Module.Assembly;

// Getting an Assembly, method 2. Load a particular assembly, using a reference to a
// module that is within that assembly. Note that this requires a compiled module for
// the reference, and when running in an aspx page, will require a fully qualifed path
// to the file, to ensure it is correctly identified
Assembly b = Assembly.LoadFrom ("GetTypes.exe");

// note that either of the above methods is viable, depending on the information
// you have. Since we know the name of the file which houses all of the base system
// objects, we could do the following to replace the first example, just as effectively
// (the absolute path may change on your machine)
// Assembly a = Assembly.LoadFrom
//			("c:/winserv/microsoft.net/framework/v1.0.2230/mscorlib.dll");
C# VB  

标识了程序集后,现在可以继续检索类型,将 GetTypes 方法的返回值分配给 Type 对象的数组。现在便可以操作这些类型了。在下面的示例中,您将获取核心运行时库的类型,并分别计算该程序集内不同类型样式的数目(如果需要有关 Foreach (For Each) 语句的更多信息,请参阅如何迭代通过集合主题下的内容)。尽管还可以计算其他成员(如类和枚举)的数目,但在该示例中,将只展示如何计算接口数目。

//Get all the types in the assembly identified in the previous example
Type [] types = a.GetTypes ();

int numInterfaces = 0;



foreach (Type t in types) {

	//the following line uses a set of methods which identify what
	//kind of type we are currently querying
	if (t.IsInterface) {
		// only print out the names of the Interfaces
		Console.WriteLine (t.Name + "");
		numInterfaces++;
	}
}

// write out the totals
Console.WriteLine("Out of {0} types in the {1} library:",
			types.Length, typeof(Object).Module.ToString());
Console.WriteLine ("{0} are interfaces (listed)", types.Length, numInterfaces);
C# VB  

还可以使用第一个示例中标识的第二种方法检索给定程序集的类型。在下面的示例中,您将注意到它并未使用相同的基结构来依次通过类型,因为您不需要跟踪类型的不同种类。当查看小型程序集时这很适合,如当前运行的应用程序(获取 MSCorLib 的所有类型的列表将得到一个非常大的列表)。

// Get all the types in the assembly identified in the previous example (this assembly)
Type [] types2 = b.GetTypes ();

Console.WriteLine ("Get all the types from the assembly: '{0}'", b.GetName());

foreach (Type t in types2)
{
    Console.WriteLine (t.FullName);
}
C# VB  

posted @ 2005-03-11 17:30 HAL9000 阅读(763) | 评论 (0)编辑