And*_*kin 14 .net reflection base-class-library .net-4.5
.NET Framework 4.5开发人员预览版中的新增内容
能够自定义反射上下文以通过CustomReflectionContext类覆盖默认反射行为.
这是什么目的ReflectionContext?MSDN在这个问题上并不十分清楚.
Ian*_*ths 39
在过去的.NET中,希望能够通过反射自动化某些功能并能够自定义它们之间存在紧张关系.例如,在Visual Studio中使用"属性"面板 - 在显示某种.NET类型(例如,设计图面上的控件)的场景中,它可以自动发现并显示该类型定义的每个公共属性.
对这种类型驱动的行为使用反射很有用,因为它意味着每个属性都会显示,而控件的开发者不需要做任何事情.但它提出了一个问题:如果您想自定义内容,例如为特定属性定义分类或自定义编辑UI,该怎么办?
.NET中的经典解决方案是将一堆自定义属性打到相关成员上.但是,其中一个问题是它可能意味着代码中的部分代码在运行时最终会执行有意义的工作,这取决于在设计时只执行任何操作的类 - 依赖于属性会阻止您分离运行时和设计时间方面.您真的想要为VS的属性面板提供自定义设计器UI的代码,作为最终用户计算机上的控件库的一部分吗?
另一个问题是,在某些情况下,您可能想要动态决定您提供的"属性".其中一个最古老的例子(可追溯到.NET 1.0)是DataSet在某种网格控制(客户端或Web)中.对于强类型数据集,反射可能是网格发现源提供的属性的适当方式,但DataSet也可以动态使用,因此您需要一种方法让数据网格在运行时询问要显示的列.
(对此的一个答案是:正确设计你的用户界面!直接生成这样的网格会导致糟糕的用户体验.但是,很多人都想以懒惰的方式去做,不管这是不是一个好主意......)
因此,您有时需要反射驱动的行为,但有时您希望能够在运行时进行全面控制.
为此出现了各种临时解决方案.您拥有整个TypeDescriptor和PropertyDescriptor一系列类型,它们在反射之上提供了一种可虚拟化的视图.默认情况下,这只会直接从反射中传递所有内容,但类型有机会选择在运行时提供自定义描述符,使他们能够修改甚至完全替换它们的外观.ICustomTypeDescriptor是这个世界的一部分.
这为默认情况下需要反射驱动行为的问题提供了一种解决方案,并提供了在需要时提供运行时驱动行为的选项.但它并没有解决您只想在设计时执行此操作的问题,并且您不希望将该代码作为运行时可再发行组件的一部分发布.
几年前,Visual Studio引入了自己的临时机制,用于在设计时增加类型信息.有一堆约定驱动的行为,Visual Studio将自动发现与特定运行时组件相关的设计时组件,使您能够自定义设计体验,而无需将相关代码烘焙到可再发行组件中.Blend也使用这种机制,虽然有一些调整,可以为VS和Blend提供不同的设计师作品.
当然,通过普通反射API看不到这一点 - VS和Blend有一个位于反射顶部的包装层,使这一切都能正常工作.
所以现在我们有两个虚拟化层可以通过反射,或者可以增加反射出来的内容......
看起来在.NET 4.5中,CLR团队决定由于各个团队已经在做这类事情,而其他团队想要做更多事情(MEF团队对反射驱动 - 可选 - 运行时有类似要求-augmentation行为),这正是应该在运行时中构建的那种东西.
新模型似乎是这样的:ReflectionContext基类是一个抽象API,通过它您可以获得反射API的虚拟化版本.这看起来很简单,因为其中一个主要想法是你不再需要像类型描述符系统这样的专用API,如果你的唯一目标是在反射之上获得可虚拟化的包装器 - 反射现在可以开箱即用.所以你可以写这种东西
public static void ShowAllAttributes(Type t)
{
foreach (Attribute attr in t.GetCustomAttributes(true))
{
Console.WriteLine(attr);
}
}
Run Code Online (Sandbox Code Playgroud)
现在你总能编写它,但在.NET 4.5之前,像这样的代码总是会对"真实"类型信息起作用,因为它使用了Reflection.但是由于反射上下文,现在可以为它提供虚拟化Type.所以考虑这个非常无聊的类型:
class NoRealAttributes
{
}
Run Code Online (Sandbox Code Playgroud)
如果您只是传递typeof(NoRealAttributes)给我的ShowAllAttributes方法,它将不打印任何内容.但我可以写一个(有点人为的)自定义反射上下文:
class MyReflectionContext : CustomReflectionContext
{
protected override IEnumerable<object> GetCustomAttributes(MemberInfo member, IEnumerable<object> declaredAttributes)
{
if (member == typeof(NoRealAttributes))
{
return new[] { new DefaultMemberAttribute("Foo") };
}
else
{
return base.GetCustomAttributes(member, declaredAttributes);
}
}
}
Run Code Online (Sandbox Code Playgroud)
(顺便说一句,我认为CustomReflectionContext它和它的基础之间的区别ReflectionContext是,后者为可虚拟化的反射上下文定义了API,同时CustomReflectionContext添加了一些帮助,使你更容易实现这样的东西.)现在我可以使用它Type为我的班级提供虚拟化版本:
var ctx = new MyReflectionContext();
Type mapped = ctx.MapType(typeof(NoRealAttributes).GetTypeInfo());
ShowAllAttributes(mapped);
Run Code Online (Sandbox Code Playgroud)
在此代码中,mapped仍然引用一个Type对象,因此任何知道如何使用反射API的东西都能够使用它,但现在它将报告实际上不存在的属性.当然,这Type是抽象的,所以我们总是有从中得到的东西,如果你打电话给mapped.GetType()你,你会发现它实际上是一个System.Reflection.Context.Custom.CustomType而不是System.RuntimeType你通常看到的东西.并且该CustomType对象属于我的自定义上下文,因此您通过它获取的任何其他反射API对象(例如,如果您编写mapped.Assembly.GetTypes()),您还将获得通过我的自定义上下文的自定义对象,这将有机会修改任何内容出来的其他东西.
因此代码可以使用自定义Type对象在类型系统中导航.即使这样的代码使用的是普通的基本反射API,我现在也有机会根据自己的需要自定义任何内容.
如果您要求,您只能获得此虚拟化视图.例如,.NET 4.5中的MEF查找自定义属性,指定它应该使用用户提供的自定义反射上下文,否则将回退到普通反射.(在我的ShowAllAttributes方法的情况下,它使用Type我选择传入的任何对象 - 它不知道它是否获得虚拟化或"真实"类型对象.)
简而言之,这意味着如果您需要虚拟化类型信息,则不再需要围绕反射API的临时包装器.