Metadata and Reflection in .NET

begin:
Posted by scott on 2004年11月10日

The .NET platform depends on metadata at design time, compile time, and execution time. This article will cover some theory of metadata, and demonstrate how to examine metadata at runtime using reflection against ASP.NET web controls.
Metadata is the life blood of the .NET platform. Many of the features we use everyday during design time, during compile time, and during execution time, rely on the presence of metadata. Metadata allows an assembly, and the types inside an assembly, to be self-describing. In this article, we will discuss metadata and look at some of types and methods used to programmatically inspect and consume metadata.

Metadata – Some History
Metadata is not new in software development. In fact, many of .NETs predecessors used metadata. COM+ for instance, kept metadata in type libraries, in the registry, and in the COM+ catalog. This metadata could describe if a component required a database transaction or if the component should participate in object pooling. However, since the COM runtime kept metadata in various locations, irregularities could occur. Also, the metadata could not fully describe a type, and was not extensible.

A compiler for the common language runtime (CLR) will generate metadata during compilation and store the metadata (in a binary format) directly into assemblies and modules to avoid irregularities. The CLR also allows metadata extensibility, meaning if you want to add custom metadata to a type or an assembly, there are mechanisms for doing so.

Metadata in .NET cannot be underestimated. Metadata allows us to write a component in C# and let another application use the metadata from Visual Basic .NET. The metadata description of a type allows the runtime to layout an object in memory, to enforce security and type safety, and ensure version compatibilities.

Metadata & Reflection
Reflection is the ability to read metadata at runtime. Using reflection, it is possible to uncover the methods, properties, and events of a type, and to invoke them dynamically. Reflection also allows us to create new types at runtime, but in the upcoming example we will be reading and invoking only.

Reflection generally begins with a call to a method present on every object in the .NET framework: GetType. The GetType method is a member of the System.Object class, and the method returns an instance of System.Type. System.Type is the primary gateway to metadata. System.Type is actually derived from another important class for reflection: the MemeberInfo class from the System.Reflection namespace. MemberInfo is a base class for many other classes who describe the properties and methods of an object, including FieldInfo, MethodInfo, ConstructorInfo, ParameterInfo, and EventInfo among others. As you might suspect from thier names, you can use these classes to inspect different aspects of an object at runtime.

Metadata : An Example
In the web form shown below we will allow the user to inspect and set any property of a web control using reflection techniques. We have a Panel control on the bottom of the form filled with an assortment of controls (in this case, a TextBox, a Button, a HyperLink, and a Label, although if you download the code you can drag any controls into the panel and watch the example work).

When the page initially loads we will loop through the Controls collection of the Panel to see what controls are available. We can then display the available controls in the ListBox and allow the user to select a control. The code to load the ListBox is shown below.

private void Page_Load(object sender, System.EventArgs e)
{
if(!Page.IsPostBack)
{
PopulateControlList();
}
}

private void PopulateControlList()
{
foreach(Control c in Panel1.Controls)
{
if(c is WebControl)
{
controlList.Items.Add(new ListItem(c.ID, c.ID));
}
}
}

You can see we test each control in the Panel to see if it derives from WebControl with the ‘is‘ keyword. If the control is a type of WebControl, we add the ID of the control to the list. The user can then select a control from the list with the mouse. We have set the list control’s AutoPostBack property to true so it will fire the SelectedIndexChanged event when this happens. During the event we want to populate a DropDownList control with the properties available on the selected object. The code to perform this task is shown next.

private void controlList_SelectedIndexChanged(object sender, System.EventArgs e)
{
PopulatePropertyList();
}

private void PopulatePropertyList()
{
propertyList.Items.Clear();
valueText.Text = String.Empty;
WebControl control = FindSelectedPanelControl();

if(control != null)
{
Type type = control.GetType();

PropertyInfo[] properties = type.GetProperties();

foreach(PropertyInfo property in properties)
{
propertyList.Items.Add(new ListItem(property.Name));
}

GetPropertyValue();
}
}

private WebControl FindSelectedPanelControl()
{
WebControl result = null;

string controlID = controlList.SelectedItem.Text;

result = Panel1.FindControl(controlID) as WebControl;

return result;
}

The first step in PopulatePropertyList is to obtain a reference to the control the user selected. We do this step in FindSelectedPanelControl by asking the list for the selected item’s Text property (which is the ID of the control). We can then pass this ID to the FindControl method to retrieve the control reference.

With the reference in hand we call GetType to obtain a Type reference. The Type class, as we mentioned before, is the gateway to obtaining metadata at runtime. By invoking the GetProperties method we obtain an array of PropertyInfo objects. The PropertyInfo class gives each object in the array a Name property, and we can populate the DropDownList with the name.

The DropDownList also has AutoPostBack set to true to let us catch the SelectedIndexChange event when the user selects a property to inspect. When this event fires we want to obtain the value of the property the user selected, as shown in the event handling code below.

private void GetPropertyValue()
{
WebControl control = FindSelectedPanelControl();
Type type = control.GetType();

string propertyName = propertyList.SelectedItem.Text;
BindingFlags flags = BindingFlags.GetProperty;
Binder binder = null;
object[] args = null;

object result = type.InvokeMember(
propertyName,
flags,
binder,
control,
args
);

valueText.Text = result.ToString();
}

Once again we will obtain a reference to the selected control using FindSelectedPanelControl, then use that control reference to obtain a Type reference for the control. Next, we need to setup parameters to invoke the property by name using InvokeMember. The InvokeMember allows us to execute methods by name (and a property is a special type of method).

The first parameter to InvokeMember is the name of the member to call. For example, “ImageUrl” is the name of a HyperLink control member. The second parameter is a BindingFlags enumeration. BindingFlags tell InvokeMember how to look for the named member. By passing BindingFlags.GetProperty we are telling InvokeMember to look for “ImageUrl” as a “get” property method, as opposed to a “set” property method (because in the runtime, get_ImageUrl and set_ImageUrl are the two methods behind the ImageUrl property).

The binder parameter for InvokeMember is a parameter we do not use, so we pass null. A binder is useful in rare circumstances where we need explicit control over how the reflection code selects a member and converts arguments. By passing a null value we are letting reflection use the default binder to perform the member lookup and argument passing.

The fourth parameter to InvokeMember is the target object instance. This is the object the runtime will try to invoke the member upon, so we pass the reference to the selected control. Finally, the last parameter is a parameter for any arguments to pass to the member. Since fetching a property does not require any parameters we can pass a null value.

InvokeMember returns an object which is the return value of the member we called (if we were to InvokeMember on a member returning void, InvokeMember returns a null value). If we InvokeMember on ImageUrl we will get back a string, we will take this string and display it in the valueText TextBox control.

If the user modifies the valueText control and clicks the “Set Property” button, we want to set the selected property with the value. The event handler for the button is shown next.

private void SetPropertyValue()
{
WebControl control = FindSelectedPanelControl();
Type type = control.GetType();

string propertyName = propertyList.SelectedItem.Text;
BindingFlags flags = BindingFlags.SetProperty;
Binder binder = null;

object arg = CoherceStringToPropertyType(control, propertyName, valueText.Text);
object[] args = { arg };

type.InvokeMember(
propertyName,
flags,
binder,
control,
args
);
}

Once again we retrieve a reference to the selected control, and a reference to the runtime type representation of the control with GetType. This time we setup the InvokeMember parameters to perform a “set property” operation. Notice the BindingFlags have chanced to BindingFlags.SetProperty. We also now have to pass an argument array, with the one argument being the value the user has typed into the textbox. We can’t just pass this string in the argument array, however, because not all of the properties take a string argument. The AutoPostBack property, for instance, takes a boolean parameter. To force the parameter into the correct type we call the method below.

private object CoherceStringToPropertyType(WebControl control,
string propertyName,
string value)
{
object result = null;

Type type = control.GetType();
PropertyInfo p = type.GetProperty(propertyName);
Type propertyType = p.PropertyType;

TypeConverter converter = TypeDescriptor.GetConverter(propertyType);
result = converter.ConvertFrom(value);

return result;
}

The method above uses a TypeConverter object. A TypeConverter is useful for converting types from a textual representation back to their original type (for the AutoPostBack property, we would convert a string to a bool). Notice we can retrieve the TypeConverter for a given property by asking the TypeDescriptor class to give us a TypeConverter. This allows us to avoid writing a large amount of code, perhaps with a switch statement, that would examine the property type to determine what kind of type we need (string, bool, int, or some class we’ve never heard of). Instead, the metadata of the type allows it to be self-describing and give us an object that can perform the specific conversion for us.

This article only touches upon some of the basic features of metadata and reflection. Here are some additional resources for more information.

Displaying Metadata in .NET EXEs with MetaViewer

Dynamically Bind Your Data Layer to Stored Procedures and SQL Commands Using .NET Metadata and Reflection

Use Reflection to Discover and Assess the Most Common Types in the .NET Framework

How Microsoft Uses Reflection

Download the code for this article: ReflectIt.zip

-- by K. Scott Allen

时间: 2024-10-31 18:18:55

Metadata and Reflection in .NET的相关文章

我所理解的Remoting(1):Marshaling & Activation[下篇]

在上面一片文章([原创]我所理解的Remoting(1):Marshaling & Activation - Part I),我花了大量的文字来来描述了Remote Object如何通过Marshaling的过程从Server端所在的Application Domain经过相关的转换(Transformation)传递到Client所在的Application Domain供Client调用: 以及Client的调用请求如何在Activate处于Server端Application Domain

C#学习笔记之四(Attribute, Reflection, Thread, Thread Syn

Atributes: //can add metadata in program, It store with the program //using ILDasm to check atributes //usage [CodeReviewAttribute("08/08/2005", "Allan Zhang", Comment = "That is a Attribute test")] class Employee {} //define

Flash AS3 中的反射 — Reflection

谢草莓和 yhc13 提供人肉翻译机支持 什么是反射反射 (Reflection) 是指在程序在运行时 (run-time) 获取类信息的方式. 诸如实现动态创建类实例, 方法等. 在很语言中都有相关的的实现, 如 Java 和 c# 等 反射有什么用在 as3 与 as2 不同, 类实例中任何元素, 如变量 (variable), 访问器 (accessor, 即 getter / setter), 方法 (method) 都是不可被 for..in 遍历的 (不是默认哦, 目前我还没找到办法

阅读器关闭时 MetaData 的尝试无效。

问题描述 "/Web"应用程序中的服务器错误.--------------------------------------------------------------------------------阅读器关闭时MetaData的尝试无效.说明:执行当前Web请求期间,出现未处理的异常.请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息.异常详细信息:System.InvalidOperationException:阅读器关闭时MetaData的尝试无效.源错

LINQ TO Reflection

引言 我们平时开发中不可避免,或者说,经常性的使用反射.但是却没有一个合适的类库帮助我们更好的利用反射.从早期的FastInvoker,到老赵的 fastreflectionlib ,无一不是在强调Fast.这是因为反射的性能损耗比较厉害,所以大家都集中精力解决性能的问题,但是在易用性方面却鲜有改进.今天我为大家带来一个即兼顾性能又具有良好的使用体验的反射类库. .Metadata() 此类库以LINQ TO Object为基础,可以通过调用.Metadata()方法获取对应Type的完整Met

Mirror C++ reflection library 0.5.12发布

Mirror C++++ 反射库提供了编译时和运行时的 C++ 程序元数据,例如命名空间.类型.枚举.类以及类成员和构造函数等.同时提供一些高级的工具用于类似工厂类生成器. Mirror C++ reflection library 0.5.12更新日志: The Mirror C++ reflection library provides both compile-time and run-time meta-data describing common C++ program constru

利用Java Reflection(反射)原理,在hibernate里面实现对单表、视图的动态组合查询

动态|视图|组合查询     Reflection 是 Java 程序开发语言的特征之一,它允许运行中的程序对自身进行访问,并能直接操作程序的内部属性.     Hibernate是一个面向Java环境的对象/关系数据库映射工具,基本上,每个表或者视图在hibernate里面都可以对应一个类,在此处,我们通过充分的利用这个类,来实现动态组合查询. 首先我们一起来看看这个函数的源代码:     /**     * 组合查询     * @param object 包含查询条件的对象     * @

.net的reflection (1)

在我的文章<C#基于接口的编程>中,我谈论了使用抽象的接口作为编程范例的各种优点.分离接口和执行过程,现在肯定不是新的思想,实际上它是com编程的核心.也许基于接口的在显著的特征是多态性和即时检查(RTTI).RTTI允许客户端程序在运行时访问对象.例如,如果一个对象执行IAthlete接口,一个客户端程序能够查找然后绑定这个接口用于调用和定位另一个接口. 查询接口是强大的,是com+的基础.同时,它对能够在一个对象上执行接口导向又是极端有用的和明确必要的,这是.net一个特别重要的概念.在介

.net的reflection (2)

一旦得到类对象,上表中所列的方法就能被叫来调用reflaction.第一个例子将检查在CSharpReflectionSamples.Reflect类中的得到方法的信息.第一块代码用来定义类中的每个方法的名字,第二块代码将阐述得到方法信息.向下面所展示的,我们将用一个数组来保存用GetMethod()方法返回的方法信息.MethodInfo类包含信息为方法的名字,不管是否是虚拟的,它都是可见的,等等. namespace CSharpReflectionSamples { using Syste