在现代软件开发中,反射(Reflection)是一种强大的机制,它允许程序在运行时检查对象的类型信息,并且可以动态地调用对象的方法或访问其属性。尽管反射非常强大,但它也相对复杂,并且使用不当可能会影响程序的性能。幸运的是,.NET框架提供了一种更简单的方式来获取对象的信息,这就是TypeDescriptor类。
TypeDescriptor是一个静态密封类,它提供了一种简便的方式来获取对象的属性、事件、类型等信息,而不需要直接使用反射。它还可以在运行时动态地扩展对象。本文将通过一个示例来展示如何使用TypeDescriptor来获取对象的信息,而不需要编写一行反射代码。
TypeDescriptor类提供了获取对象信息的方法,例如属性、事件等。要使用它,需要将一个组件传递给它的静态方法。以下是一个简单的例子:
C# Button b = new Button();
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(b);
EventDescriptorCollection events = TypeDescriptor.GetEvents(b);
foreach (PropertyDescriptor pd in props)
Console.WriteLine(pd.DisplayName);
foreach (EventDescriptor ed in events)
Console.WriteLine(ed.Name);
Console.ReadLine();
在上面的代码中,创建了一个Button类的对象,并获取了它的信息(在这个例子中是名称)。GetProperties方法可以接收一个对象或对象的Type,以列出它所有的属性,返回一个PropertyDescriptorCollection集合。类似地,GetEvents方法会列出所有的事件。
EventDescriptor类定义了对象的一个事件。与反射命名空间中的EventInfo类不同,EventDescriptor提供了一个更简单的接口来实现相同的功能。EventDescriptor类暴露了一些特性,例如:
EventDescriptor还可以用来添加或移除事件处理器。例如:
C# EventDescriptor d = TypeDescriptor.GetDefaultEvent(b);
d.AddEventHandler(b, new EventHandler(b_Click));
b.PerformClick();
d.RemoveEventHandler(b, new EventHandler(b_Click));
b.PerformClick();
static void b_Click(object sender, EventArgs e)
{
Console.WriteLine("Clicked");
}
这段代码会打印出“Clicked”一次,因为使用了RemoveEventHandler来移除Click事件处理器。
如果不熟悉默认属性或默认事件,它们是属性或事件的特殊属性。DefaultPropertyAttribute是一个特殊属性,它在类级别设置,指示默认使用的属性。它在Visual Studio的属性窗口中使用。DefaultEventAttribute用于事件。
PropertyDescriptor类提供了关于属性的信息。可以使用PropertyDescriptor来设置值、获取值、重置值等。以下是PropertyDescriptor的一些选项:
除了这些,PropertyDescriptor还维护了一个内部的委托集合,这让可以在属性更改时得到通知。
C# PropertyDescriptor p = TypeDescriptor.GetDefaultProperty(b);
p.AddValueChanged(b, new EventHandler(b_Click));
p.AddValueChanged(b, new EventHandler(b_Click));
b.Text = "This is changed";
p.RemoveValueChanged(b, new EventHandler(b_Click));
static void b_Click(object sender, EventArgs e)
{
Console.WriteLine("Changed");
}
在这个例子中,故意添加了两次AddValueChanged,因此b_click方法将被调用两次(每次AddValueChanged一次),用于单个Text更改(Text是Button类的默认属性)。
除了上述列出的内容,TypeDescriptor还允许做很多其他工作。它提供了一个接口来获取属性、定义一个对象与其他对象的关联等,而大多数其他好处与VS IDE的设计实现相关。将在另一篇帖子中回顾它们。
在讨论了Descriptors之后,让用一段代码来结束这个话题,这段代码可能对很有用。经常需要评估一个对象的属性树,但使用反射通常会变得非常复杂。让看看下面的代码:
C# static T EvaluateProperty(object container, string property)
{
string[] expressionPath = property.Split('.');
object baseobject = container;
for (var i = 0; i < expressionPath.Length; i++)
{
string currentProperty = expressionPath[i];
if (!string.IsNullOrEmpty(currentProperty))
{
PropertyDescriptorCollection descriptorcollection = TypeDescriptor.GetProperties(baseobject);
PropertyDescriptor descriptor = descriptorcollection.Find(currentProperty, true);
baseobject = descriptor.GetValue(baseobject);
}
}
return (T)baseobject;
}
代码非常简单,将传递的字符串参数用点(.)分割,并循环遍历以获取对象的每个属性的值,最后返回实际的值。因此,如果想评估一个像这样的大属性字符串:
C# MyClass x = new MyClass();
EndResult result = x.MyProperty.MyNest1Property.MyNest2Property.MyNest3Property;
C# EndResult result = Evaluate<EndResult>(x, "MyProperty.MyNest1Property.MyNest2Property.MyNest3Property");