USING REFLECTION
In this section, you take a closer look at the System.Type class, which enables you to access information concerning the definition of any data type. You'll also look at the System.Reflection.Assembly class, which you can use to access information about an assembly or to load that assembly into your program. Finally, you will combine the code in this section with the code in the previous section to complete the WhatsNewAttributes example.
The System.Type Class
So far you have used the Type class only to hold the reference to a type as follows:
Type t = typeof(double);
Although previously referred to as a class, Type is an abstract base class. Whenever you instantiate a Type object, you are actually instantiating a class derived from Type. Type has one derived class corresponding to each actual data type, though in general the derived classes simply provide different overloads of the various Type methods and properties that return the correct data for the corresponding data type. They do not typically add new methods or properties. In general, there are three common ways to obtain a Type reference that refers to any given type.
You can use the C# typeof operator as shown in the preceding code. This operator takes the name of the type (not in quotation marks, however) as a parameter.
You can use the GetType method, which all classes inherit from System.Object:
double d = 10;
Type t = d.GetType();GetType is called against a variable, rather than taking the name of a type. Note, however, that the Type object returned is still associated with only that data type. It does not contain any information that relates to that instance of the type. The GetType method can be useful if you have a reference to an object but you are not sure what class that object is actually an instance of.
You can call the static method of the Type class, GetType:
Type t = Type.GetType("System.Double");
Type is really the gateway to much of the reflection functionality. It implements a huge number of methods and properties—far too many to provide a comprehensive list here. However, the following subsections should give you a good idea of the kinds of things you can do with the Type class. Note that the available properties are all read-only; you use Type to find out about the data type—you cannot use it to make any modifications to the type!
Type Properties
You can divide the properties implemented by Type into three categories. First, a number of properties retrieve the strings containing various names associated with the class, as shown in the following table:
PROPERTY |
RETURNS |
---|---|
Name |
The name of the data type |
FullName |
The fully qualified name of the data type (including the namespace name) |
Namespace |
The name of the namespace in which the data type is defined |
Second, it is possible to retrieve references to further type objects that represent related classes, as shown in the following table.
PROPERTY |
RETURNS TYPE REFERENCE CORRESPONDING TO |
---|---|
BaseType |
The immediate base type of this type |
UnderlyingSystemType |
The type to which this type maps in the .NET runtime (recall that certain .NET base types actually map to specific predefined types recognized by IL) |
A number of Boolean properties indicate whether this type is, for example, a class, an enum, and so on. These properties include IsAbstract, IsArray, IsClass, IsEnum, IsInterface, IsPointer, IsPrimitive (one of the predefined primitive data types), IsPublic, IsSealed, and IsValueType. The following example uses a primitive data type:
Type intType = typeof(int);
Console.WriteLine(intType.IsAbstract); // writes false
Console.WriteLine(intType.IsClass); // writes false
Console.WriteLine(intType.IsEnum); // writes false
Console.WriteLine(intType.IsPrimitive); // writes true
Console.WriteLine(intType.IsValueType); // writes true
This example uses the Vector class:
Type vecType = typeof(Vector);
Console.WriteLine(vecType.IsAbstract); // writes false
Console.WriteLine(vecType.IsClass); // writes true
Console.WriteLine(vecType.IsEnum); // writes false
Console.WriteLine(vecType.IsPrimitive); // writes false
Console.WriteLine(vecType.IsValueType); // writes false
Finally, you can also retrieve a reference to the assembly in which the type is defined. This is returned as a reference to an instance of the System.Reflection.Assembly class, which is examined shortly:
Type t = typeof (Vector);
Assembly contai6ningAssembly = new Assembly(t);
Methods
Most of the methods of System.Type are used to obtain details about the members of the corresponding data type—the constructors, properties, methods, events, and so on. Quite a large number of methods exist, but they all follow the same pattern. For example, two methods retrieve details about the methods of the data type: GetMethod and GetMethods.GetMethod() returns a reference to a System.Reflection.MethodInfo object, which contains details about a method. GetMethods returns an array of such references. As the names suggest, the difference is that GetMethods returns details about all the methods, whereas GetMethod returns details about just one method with a specified parameter list. Both methods have overloads that take an extra parameter, a BindingFlags enumerated value that indicates which members should be returned — for example, whether to return public members, instance members, static members, and so on.
For example, the simplest overload of GetMethods takes no parameters and returns details about all the public methods of the data type:
Type t = typeof(double);
MethodInfo[] methods = t.GetMethods();
foreach (MethodInfo nextMethod in methods)
{
// etc.
}
The member methods of Type that follow the same pattern are shown in the following table. Note that plural names return an array.
TYPE OF OBJECT RETURNED |
METHOD(S) |
---|---|
ConstructorInfo |
GetConstructor(), GetConstructors() |
EventInfo |
GetEvent(), GetEvents() |
FieldInfo |
GetField(), GetFields() |
MemberInfo |
GetMember(), GetMembers(), GetDefaultMembers() |
MethodInfo |
GetMethod(), GetMethods() |
PropertyInfo |
GetProperty(), GetProperties() |
The GetMember and GetMembers methods return details about any or all members of the data type, regardless of whether these members are constructors, properties, methods, and so on.
The TypeView Example
This section demonstrates some of the features of the Type class with a short example, TypeView, which you can use to list the members of a data type. The example demonstrates how to use TypeView for a double; however, you can swap this type with any other data type just by changing one line of the code in the example. TypeView displays far more information than can be displayed in a console window, so we're going to take a break from our normal practice and display the output in a message box. Running TypeView for a double produces the results shown in Figure 15-1.
Figure 15-1
The message box displays the name, full name, and namespace of
the data type as well as the name of the underlying type and the base type.
Next, it simply iterates through all the public instance members of the data
type, displaying for each member the declaring type, the type of member (method,
field, and so on), and the name of the member. The declaring
type is the name of the class that actually declares the type member (for
example, System.Double if it is defined or overridden in
System.Double, or the name of the relevant base type if the member is
simply inherited from a base class).
TypeView does not display signatures of methods
because you are retrieving details about all public instance members through
MemberInfo objects, and information about parameters is not available
through a MemberInfo object. To retrieve that information, you would
need references to MethodInfo and other more specific objects, which
means that you would need to obtain details about each type of member
separately.
TypeView does display details about all public
instance members; but for doubles, the only ones defined are fields and methods.
For this example, you will compile TypeView as a console application —
there is no problem with displaying a message box from a console application.
However, because you are using a message box, you need to reference the base
class assembly System.Windows.Forms.dll, which contains the classes in
the System.Windows.Forms namespace in which the MessageBox
class that you will need is defined. The code for TypeView is as
follows. To begin, you need to add a few using statements:
using System;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
You need System.Text because you will be using a StringBuilder object to build up the text to be displayed in the message box, and System.Windows.Forms for the message box itself. The entire code is in one class, MainClass, which has a couple of static methods and one static field, a StringBuilder instance called OutputText, which will be used to build the text to be displayed in the message box. The main method and class declaration look like this:
class MainClass
{
static StringBuilder OutputText = new StringBuilder(); static void Main()
{
// modify this line to retrieve details of any
// other data type
Type t = typeof(double); AnalyzeType(t);
MessageBox.Show(OutputText.ToString(), "Analysis of type "
+ t.Name);
Console.ReadLine();
}
The Main method implementation starts by declaring a Type object to represent your chosen data type. You then call a method, AnalyzeType, which extracts the information from the Type object and uses it to build the output text. Finally, you show the output in a message box. Using the MessageBox class is fairly intuitive. You just call its static Show method, passing it two strings, which will, respectively, be the text in the box and the caption. AnalyzeType is where the bulk of the work is done:
static void AnalyzeType(Type t)
{
AddToOutput("Type Name: " + t.Name);
AddToOutput("Full Name: " + t.FullName);
AddToOutput("Namespace: " + t.Namespace); Type tBase = t.BaseType; if (tBase != null)
{
AddToOutput("Base Type:" + tBase.Name);
} Type tUnderlyingSystem = t.UnderlyingSystemType; if (tUnderlyingSystem != null)
{
AddToOutput("UnderlyingSystem Type:" + tUnderlyingSystem.Name);
} AddToOutput("\nPUBLIC MEMBERS:");
MemberInfo [] Members = t.GetMembers(); foreach (MemberInfo NextMember in Members)
{
AddToOutput(NextMember.DeclaringType + " " +
NextMember.MemberType + " " + NextMember.Name);
}
}
You implement the AnalyzeType method by calling various properties of the Type object to get the information you need concerning the type names, then call the GetMembers method to get an array of MemberInfo objects that you can use to display the details for each member. Note that you use a helper method, AddToOutput, to build the text to be displayed in the message box:
static void AddToOutput(string Text)
{
OutputText.Append("\n" + Text);
}
Compile the TypeView assembly using this command:
csc /reference:System.Windows.Forms.dll Program.cs
The Assembly Class
The Assembly class is defined in the System.Reflection namespace and provides access to the metadata for a given assembly. It also contains methods that enable you to load and even execute an assembly — assuming that the assembly is an executable. As with the Type class, Assembly contains too many methods and properties to cover here, so this section is confined to covering those methods and properties that you need to get started and that you will use to complete the WhatsNewAttributes example.
Before you can do anything with an Assembly instance, you need to load the corresponding assembly into the running process. You can do this with either the static members Assembly.Load or Assembly.LoadFrom. The difference between these methods is that Load takes the name of the assembly, and the runtime searches in a variety of locations in an attempt to locate the assembly. These locations include the local directory and the global assembly cache. LoadFrom takes the full path name of an assembly and does not attempt to find the assembly in any other location:
Assembly assembly1 = Assembly.Load("SomeAssembly");
Assembly assembly2 = Assembly.LoadFrom
(@"C:\My Projects\Software\SomeOtherAssembly");
A number of other overloads of both methods exist, which supply additional security information. After you have loaded an assembly, you can use various properties on it to find out, for example, its full name:
string name = assembly1.FullName;
Getting Details About Types Defined in an Assembly
One nice feature of the Assembly class is that it enables you to obtain details about all the types that are defined in the corresponding assembly. You simply call the Assembly.GetTypes method, which returns an array of System.Type references containing details about all the types. You can then manipulate these Type references as explained in the previous section:
Type[] types = theAssembly.GetTypes(); foreach(Type definedType in types)
{
DoSomethingWith(definedType);
}
Getting Details About Custom Attributes
The methods you use to find out which custom attributes are defined on an assembly or type depend on the type of object to which the attribute is attached. If you want to find out what custom attributes are attached to an assembly as a whole, you need to call a static method of the Attribute class, GetCustomAttributes, passing in a reference to the assembly:
Note |
This is actually quite significant. You may have wondered why, when you defined custom attributes, you had to go to all the trouble of actually writing classes for them, and why Microsoft didn't come up with some simpler syntax. Well, the answer is here. The custom attributes genuinely exist as objects, and when an assembly is loaded you can read in these attribute objects, examine their properties, and call their methods |
Attribute[] definedAttributes =
Attribute.GetCustomAttributes(assembly1);
// assembly1 is an Assembly object
GetCustomAttributes, which is used to get assembly attributes, has a few overloads. If you call it without specifying any parameters other than an assembly reference, it simply returns all the custom attributes defined for that assembly. You can also call GetCustomAttributes by specifying a second parameter, which is a Type object that indicates the attribute class in which you are interested. In this case, GetCustomAttributes returns an array consisting of all the attributes present that are of the specified type.
Note that all attributes are retrieved as plain Attribute references. If you want to call any of the methods or properties you defined for your custom attributes, you need to cast these references explicitly to the relevant custom attribute classes. You can obtain details about custom attributes that are attached to a given data type by calling another overload of Assembly.GetCustomAttributes, this time passing a Type reference that describes the type for which you want to retrieve any attached attributes. To obtain attributes that are attached to methods, constructors, fields, and so on, however, you need to call a GetCustomAttributes method that is a member of one of the classes MethodInfo, ConstructorInfo, FieldInfo, and so on.
If you expect only a single attribute of a given type, you can call the GetCustomAttribute method instead, which returns a single Attribute object. You will use GetCustomAttribute in the WhatsNewAttributes example to find out whether the SupportsWhatsNew attribute is present in the assembly. To do this, you call GetCustomAttribute, passing in a reference to the WhatsNewAttributes assembly, and the type of the SupportsWhatsNewAttribute attribute. If this attribute is present, you get an Attribute instance. If no instances of it are defined in the assembly, you get null. If two or more instances are found, GetCustomAttribute throws a System.Reflection.AmbiguousMatchException. This is what that call would look like:
Attribute supportsAttribute =
Attribute.GetCustomAttributes(assembly1,
typeof(SupportsWhatsNewAttribute));
Completing the WhatsNewAttributes Example
You now have enough information to complete the WhatsNewAttributes example by writing the source code for the final assembly in the sample, the LookUpWhatsNew assembly. This part of the application is a console application. However, it needs to reference the other assemblies of WhatsNewAttributes and VectorClass. Although this is going to be a command-line application, you will follow the previous TypeView example in that you actually display the results in a message box because there is a lot of text output—too much to show in a console window screenshot.
The file is called LookUpWhatsNew.cs, and the command to compile it is as follows:
csc /reference:WhatsNewAttributes.dll /reference:VectorClass.dll LookUpWhatsNew.cs
In the source code of this file, you first indicate the namespaces you want to infer. System.Text is there because you need to use a StringBuilder object again:
using System;
using System.Reflection;
using System.Windows.Forms;
using System.Text;
using WhatsNewAttributes; namespace LookUpWhatsNew
{
The class that contains the main program entry point as well as the other methods is WhatsNewChecker. All the methods you define are in this class, which also has two static fields — outputText, which contains the text as you build it in preparation for writing it to the message box, and backDateTo, which stores the date you have selected. All modifications made since this date will be displayed. Normally, you would display a dialog inviting the user to pick this date, but we don't want to get sidetracked into that kind of code. For this reason, backDateTo is hard-coded to a value of 1 Feb 2010. You can easily change this date when you download the code:
internal class WhatsNewChecker
{
private static readonly StringBuilder outputText = new StringBuilder(1000);
private static DateTime backDateTo = new DateTime(2010, 2, 1); static void Main()
{
Assembly theAssembly = Assembly.Load("VectorClass");
Attribute supportsAttribute =
Attribute.GetCustomAttribute(
theAssembly, typeof(SupportsWhatsNewAttribute));
string name = theAssembly.FullName; AddToMessage("Assembly: " + name); if (supportsAttribute == null)
{
AddToMessage(
"This assembly does not support WhatsNew attributes");
return;
}
else
{
AddToMessage("Defined Types:");
} Type[] types = theAssembly.GetTypes(); foreach(Type definedType in types)
DisplayTypeInfo(definedType); MessageBox.Show(outputText.ToString(),
"What\'s New since " + backDateTo.ToLongDateString());
Console.ReadLine();
}
The Main method first loads the VectorClass assembly, and then verifies that it is marked with the SupportsWhatsNew attribute. You know VectorClass has the SupportsWhatsNew attribute applied to it because you have only recently compiled it, but this is a check that would be worth making if users were given a choice of which assembly they wanted to check.
Assuming that all is well, you use the Assembly.GetTypes method to get an array of all the types defined in this assembly, and then loop through them. For each one, you call a method, DisplayTypeInfo, which adds the relevant text, including details regarding any instances of LastModifiedAttribute, to the outputText field. Finally, you show the message box with the complete text. The DisplayTypeInfo method looks like this:
private static void DisplayTypeInfo(Type type)
{
// make sure we only pick out classes
if (!(type.IsClass))
{
return;
} AddToMessage("\nclass " + type.Name); Attribute [] attribs = Attribute.GetCustomAttributes(type); if (attribs.Length == 0)
{
AddToMessage("No changes to this class\n");
}
else
{
foreach (Attribute attrib in attribs)
{
WriteAttributeInfo(attrib);
}
} MethodInfo [] methods = type.GetMethods();
AddToMessage("CHANGES TO METHODS OF THIS CLASS:"); foreach (MethodInfo nextMethod in methods)
{
object [] attribs2 =
nextMethod.GetCustomAttributes(
typeof(LastModifiedAttribute), false); if (attribs2 != null)
{
AddToMessage(
nextMethod.ReturnType + " " + nextMethod.Name + "()");
foreach (Attribute nextAttrib in attribs2)
{
WriteAttributeInfo(nextAttrib);
}
}
}
}
Notice that the first thing you do in this method is check whether the Type reference you have been passed actually represents a class. Because, to keep things simple, you have specified that the LastModified attribute can be applied only to classes or member methods, you would be wasting time by doing any processing if the item is not a class (it could be a class, delegate, or enum).
Next, you use the Attribute.GetCustomAttributes method to determine whether this class has any LastModifiedAttribute instances attached to it. If so, you add their details to the output text, using a helper method, WriteAttributeInfo.
Finally, you use the Type.GetMethods method to iterate through all the member methods of this data type, and then do the same with each method as you did for the class — check whether it has any LastModifiedAttribute instances attached to it; if so, you display them using WriteAttributeInfo.
The next bit of code shows the WriteAttributeInfo method, which is responsible for determining what text to display for a given LastModifiedAttribute instance. Note that this method is passed an Attribute reference, so it needs to cast this to a LastModifiedAttribute reference first. After it has done that, it uses the properties that you originally defined for this attribute to retrieve its parameters. It confirms that the date of the attribute is sufficiently recent before actually adding it to the text for display:
private static void WriteAttributeInfo(Attribute attrib)
{ LastModifiedAttribute lastModifiedAttrib =
attrib as LastModifiedAttribute; if (lastModifiedAttrib == null)
{
return;
}
// check that date is in range
DateTime modifiedDate = lastModifiedAttrib.DateModified; if (modifiedDate < backDateTo)
{
return;
} AddToMessage(" MODIFIED: " +
modifiedDate.ToLongDateString() + ":");
AddToMessage(" " + lastModifiedAttrib.Changes); if (lastModifiedAttrib.Issues != null)
{
AddToMessage(" Outstanding issues:" +
lastModifiedAttrib.Issues);
}
}
Finally, here is the helper AddToMessage method:
static void AddToMessage(string message)
{
outputText.Append("\n" + message);
}
}
}
Running this code produces the results shown in Figure 15-2.
Figure 15-2
Note that when you list the types defined in the
VectorClass assembly, you actually pick up two classes: Vector
and the embedded VectorEnumerator class. In addition, note that because
the backDateTo date of 1 Feb is hard-coded in this example, you
actually pick up the attributes that are dated 14 Feb (when you added the
collection support) but not those dated 10 Feb (when you added the
IFormattable interface).
USING REFLECTION的更多相关文章
- Fresnel Reflection - 菲涅尔反射
[Fresnel Reflection - 菲涅尔反射] “菲涅尔”是一个人的名字,因为他发现了一个有关反射的光学现象,这个现象就用这个人的名字命名了.那么,是什么现象呢? 这就是反射/折射与视点角度 ...
- CSharpGL(13)用GLSL实现点光源(point light)和平行光源(directional light)的漫反射(diffuse reflection)
CSharpGL(13)用GLSL实现点光源(point light)和平行光源(directional light)的漫反射(diffuse reflection) 2016-08-13 由于CSh ...
- Scala Reflection - Mirrors,ClassTag,TypeTag and WeakTypeTag
反射reflection是程序对自身的检查.验证甚至代码修改功能.反射可以通过它的Reify功能来实时自动构建生成静态的Scala实例如:类(class).方法(method).表达式(express ...
- [.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦
[.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦 本节导读:上篇文章简单介绍了.NET面向对象中一个重要的技术反射的基本应用,它可以让我们动态的调 ...
- [.net 面向对象程序设计进阶] (20) 反射(Reflection)(上)利用反射技术实现动态编程
[.net 面向对象程序设计进阶] (20) 反射(Reflection)(上)利用反射技术实现动态编程 本节导读:本节主要介绍什么是.NET反射特性,.NET反射能为我们做些什么,最后介绍几种常用的 ...
- Could not load type 'System.Reflection.AssemblySignatureKeyAttribute' from assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c
错误: Could not load type 'System.Reflection.AssemblySignatureKeyAttribute' from assembly 'mscorlib, V ...
- 代替Reflection(反射)的一些方法
Reflection(反射)是深入学习.Net必须掌握的技能之一.最初学Reflection的时候,的确是被惊住了,原来还可以这样.只要给你一个Assembly, 你就能获取到其中所有的类型,根据类型 ...
- Leetcode: Line Reflection
Given n points on a 2D plane, find if there is such a line parallel to y-axis that reflect the given ...
- 反射(Reflection)
反射主要用于在程序运行期间动态解析相关类的类名,命名空间,属性,方法并进行相应操作,以下通过两个简单的例子进行了说明: 示例1:调用程序集内部方法,运行时动态获取相关类的信息,包括类名,命名空间等信息 ...
- 异常:“System.Reflection.Metadata”已拥有为“System.Collections.Immutable”定义的依赖项
参考动态执行T4模板:https://msdn.microsoft.com/zh-cn/library/bb126579.aspx 我项目是.NET Framework 4.5控制台应用程序写的. 执 ...
随机推荐
- POJ 2976 Dropping test(01分数规划模板)
01分数划分详情可阅读:http://www.cnblogs.com/perseawe/archive/2012/05/03/01fsgh.html 题意: 给出n个a和b,让选出n-k个使得最大 二 ...
- Leetcode 300.最长上升子序列
最长上升子序列 给定一个无序的整数数组,找到其中最长上升子序列的长度. 示例: 输入: [10,9,2,5,3,7,101,18] 输出: 4 解释: 最长的上升子序列是 [2,3,7,101],它的 ...
- Laya 利用JS进行反射
Laya 利用JS进行反射 @author ixenos 当需要配表调用函数时,可以利用js的eval来调用 1.在配置js中写下: function callAsFunc(funcName){ ev ...
- php对象(继承,多态)
/2.继承//function abc(){// $arr = func_get_args();//}//子类只能有一个父类 一个父类 可以有多个子类//override 重写//overlood 重 ...
- CodeForces230A
题目大意: 一个打恐龙的游戏,有初始体力s,和恐龙n只,然后输入n只恐龙的体力xi,和击杀它得到的奖励体力yi,只有自身体力大于恐龙体力时才能进行击杀,击杀恐龙的顺序可以不定 这题运用到了贪心的思想, ...
- poj 1163 数塔
#include<stdio.h> #include<string.h> #define N 110 int dp[N][N]; int a[N][N]; int Max(in ...
- numpy模块
NumPy简介: NumPy 是高性能科学计算和数据分析的基础包:它是pandas等其他工具的基础. NumPy的主要功能: 1. ndarray,一个多维数组结构,高效且节省空间 (最主要的功能) ...
- wordpress优化:Gravatar头像被墙及解决方案
网站缓存现象: 打开网站是左下角出现0.gravatar.com.1.gravatar.com或2.gravatar.com字样,网站一直处于缓存状态,迟迟未能打开.很多人都会缺乏耐心地等待一个网页的 ...
- python学习之-- importlib模块
importlib 模块 Python提供了importlib包作为标准库的一部分.目的就是提供Python中import语句的实现(以及__import__函数).另外,importlib允许程序员 ...
- P1546||2627 最短网络 Agri-Net 洛谷||codevs
https://www.luogu.org/problem/show?pid=1546 同http://codevs.cn/problem/2627/ 不同算法 题目背景 农民约翰被选为他们镇的镇长! ...