.net 类型描述提供器,主要功能:
how to use :
[TypeDescriptionProvider(typeof(HyperTypeDescriptionProvider))]
public class User
{
public int Id
{
get;
set;
}
public string Name
{
get;
set;
}
public Security MySecurity
{
get;
set;
}
}
public class Security
{
public string Isin
{
get;
set;
}
public DateTime MaturityDate
{
get;
set;
}
}
Check what you will see :
static void Main(string[] args)
{
PropertyDescriptorCollection col = TypeDescriptor.GetProperties(typeof(User));
foreach (PropertyDescriptor item in col)
{
Console.WriteLine(item.Name, item.Category);
}
Console.ReadLine();
}
source code begins here:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
namespace ConsoleApplication1.HyperDescriptor
{
public class HyperTypeDescriptor : CustomTypeDescriptor
{
private static int s_counter;
private static readonly Dictionary s_propertyDescriptors;
private static readonly ModuleBuilder s_moduleBuilder;
private static readonly Type s_reflectPropertyDescriptorType;
private readonly PropertyDescriptorCollection m_propertyDescriptorCollection;
static HyperTypeDescriptor()
{
s_propertyDescriptors = new Dictionary();
s_reflectPropertyDescriptorType = Assembly.GetAssembly(typeof(PropertyDescriptor)).GetType("System.ComponentModel.ReflectPropertyDescriptor");
AssemblyName an = new AssemblyName("Hyper.ComponentModel.dynamic");
AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
s_moduleBuilder = ab.DefineDynamicModule("Hyper.ComponentModel.dynamic.dll");
}
internal HyperTypeDescriptor(ICustomTypeDescriptor parent)
:base(parent)
{
m_propertyDescriptorCollection = WrapProperties(parent.GetProperties());
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return m_propertyDescriptorCollection;
}
public override PropertyDescriptorCollection GetProperties()
{
return m_propertyDescriptorCollection;
}
private static PropertyDescriptorCollection WrapProperties(PropertyDescriptorCollection oldProps)
{
var newProps = new List();
var changed = false;
//HACK:how to identify reflection,given the class internal
foreach (PropertyDescriptor oldProp in oldProps)
{
var pd = oldProp;
if (ReferenceEquals(s_reflectPropertyDescriptorType, pd.GetType()) && TryCreatePropertyDescriptor(ref pd))
changed = true;
newProps.Add(pd);
AddChildDescriptors(newProps, pd);
}
return changed ? new PropertyDescriptorCollection(newProps.ToArray(), true) : oldProps;
}
private static void AddChildDescriptors(List newProps, PropertyDescriptor pd)
{
var type = pd.PropertyType;
//don't expand properties for standard .net types
if ("CommonLanguageRuntimeLibrary".Equals(type.Module.ScopeName, StringComparison.OrdinalIgnoreCase))
return;
var properties = TypeDescriptor.GetProperties(type);
foreach (PropertyDescriptor p in properties)
{
var pp = new PathPropertyDescriptor(pd, p);
newProps.Add(pp);
}
}
private static bool TryCreatePropertyDescriptor(ref PropertyDescriptor descriptor)
{
try
{
PropertyInfo property = descriptor.ComponentType.GetProperty(descriptor.Name);
if (property == null) return false;
lock (s_propertyDescriptors)
{
PropertyDescriptor foundBuiltAlready;
if (s_propertyDescriptors.TryGetValue(property, out foundBuiltAlready))
{
descriptor = foundBuiltAlready;
return true;
}
string name = "_c" + Interlocked.Increment(ref s_counter).ToString();
TypeBuilder tb = s_moduleBuilder.DefineType(name,
TypeAttributes.Sealed | TypeAttributes.NotPublic
| TypeAttributes.Class | TypeAttributes.BeforeFieldInit
| TypeAttributes.AutoClass | TypeAttributes.Public,
typeof(ChainingPropertyDescriptor));
// ctor calls base
ConstructorBuilder cb = tb.DefineConstructor(MethodAttributes.HideBySig |
MethodAttributes.Public | MethodAttributes.SpecialName |
MethodAttributes.RTSpecialName,
CallingConventions.Standard, new Type[] { typeof(PropertyDescriptor) });
ILGenerator il = cb.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Call,
typeof(ChainingPropertyDescriptor).GetConstructor(
BindingFlags.NonPublic
| BindingFlags.Instance, null,
new Type[] { typeof(PropertyDescriptor) }, null));
il.Emit(OpCodes.Ret);
MethodBuilder mb;
MethodInfo baseMethod;
if (property.CanRead)
{
// obtain the implementation that we want to override
baseMethod = typeof(ChainingPropertyDescriptor).GetMethod("GetValue");
// create a new method that accepts an object and returns an object (as per the base)
mb = tb.DefineMethod(baseMethod.Name,
MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final,
baseMethod.CallingConvention, baseMethod.ReturnType, new Type[] { typeof(object) });
// start writing IL into the method
il = mb.GetILGenerator();
if (property.DeclaringType.IsValueType)
{
// upbox the object argument into our known (instance) struct type
LocalBuilder lb = il.DeclareLocal(property.DeclaringType);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Unbox_Any, property.DeclaringType);
il.Emit(OpCodes.Stloc_0);
il.Emit(OpCodes.Ldloca_S, lb);
}
else
{
// cast the object argument into our known class type
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Castclass, property.DeclaringType);
}
// call the "get" method
il.Emit(OpCodes.Callvirt, property.GetGetMethod());
if (property.PropertyType.IsValueType)
{
// box it from the known (value) struct type
il.Emit(OpCodes.Box, property.PropertyType);
}
// return the value
il.Emit(OpCodes.Ret);
// signal that this method should override the base
tb.DefineMethodOverride(mb, baseMethod);
}
bool supportsChangeEvents = descriptor.SupportsChangeEvents, isReadOnly = descriptor.IsReadOnly;
// override SupportsChangeEvents
baseMethod = typeof(ChainingPropertyDescriptor).GetProperty("SupportsChangeEvents").GetGetMethod();
mb = tb.DefineMethod(baseMethod.Name, MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.SpecialName, baseMethod.CallingConvention, baseMethod.ReturnType, Type.EmptyTypes);
il = mb.GetILGenerator();
if (supportsChangeEvents)
{
il.Emit(OpCodes.Ldc_I4_1);
}
else
{
il.Emit(OpCodes.Ldc_I4_0);
}
il.Emit(OpCodes.Ret);
tb.DefineMethodOverride(mb, baseMethod);
// override IsReadOnly
baseMethod = typeof(ChainingPropertyDescriptor).GetProperty("IsReadOnly").GetGetMethod();
mb = tb.DefineMethod(baseMethod.Name, MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.SpecialName, baseMethod.CallingConvention, baseMethod.ReturnType, Type.EmptyTypes);
il = mb.GetILGenerator();
if (isReadOnly)
{
il.Emit(OpCodes.Ldc_I4_1);
}
else
{
il.Emit(OpCodes.Ldc_I4_0);
}
il.Emit(OpCodes.Ret);
tb.DefineMethodOverride(mb, baseMethod);
/* REMOVED: PropertyType, ComponentType; actually *adds* time overriding these
// override PropertyType
baseMethod = typeof(ChainingPropertyDescriptor).GetProperty("PropertyType").GetGetMethod();
mb = tb.DefineMethod(baseMethod.Name, MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.SpecialName, baseMethod.CallingConvention, baseMethod.ReturnType, Type.EmptyTypes);
il = mb.GetILGenerator();
il.Emit(OpCodes.Ldtoken, descriptor.PropertyType);
il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
il.Emit(OpCodes.Ret);
tb.DefineMethodOverride(mb, baseMethod);
// override ComponentType
baseMethod = typeof(ChainingPropertyDescriptor).GetProperty("ComponentType").GetGetMethod();
mb = tb.DefineMethod(baseMethod.Name, MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.SpecialName, baseMethod.CallingConvention, baseMethod.ReturnType, Type.EmptyTypes);
il = mb.GetILGenerator();
il.Emit(OpCodes.Ldtoken, descriptor.ComponentType);
il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
il.Emit(OpCodes.Ret);
tb.DefineMethodOverride(mb, baseMethod);
*/
// for classes, implement write (would be lost in unbox for structs)
if (!property.DeclaringType.IsValueType)
{
if (!isReadOnly && property.CanWrite)
{
// override set method
baseMethod = typeof(ChainingPropertyDescriptor).GetMethod("SetValue");
mb = tb.DefineMethod(baseMethod.Name, MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, baseMethod.CallingConvention, baseMethod.ReturnType, new Type[] { typeof(object), typeof(object) });
il = mb.GetILGenerator();
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Castclass, property.DeclaringType);
il.Emit(OpCodes.Ldarg_2);
if (property.PropertyType.IsValueType)
{
il.Emit(OpCodes.Unbox_Any, property.PropertyType);
}
else
{
il.Emit(OpCodes.Castclass, property.PropertyType);
}
il.Emit(OpCodes.Callvirt, property.GetSetMethod());
il.Emit(OpCodes.Ret);
tb.DefineMethodOverride(mb, baseMethod);
}
if (supportsChangeEvents)
{
EventInfo ei = property.DeclaringType.GetEvent(property.Name + "Changed");
if (ei != null)
{
baseMethod = typeof(ChainingPropertyDescriptor).GetMethod("AddValueChanged");
mb = tb.DefineMethod(baseMethod.Name, MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.SpecialName, baseMethod.CallingConvention, baseMethod.ReturnType, new Type[] { typeof(object), typeof(EventHandler) });
il = mb.GetILGenerator();
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Castclass, property.DeclaringType);
il.Emit(OpCodes.Ldarg_2);
il.Emit(OpCodes.Callvirt, ei.GetAddMethod());
il.Emit(OpCodes.Ret);
tb.DefineMethodOverride(mb, baseMethod);
baseMethod = typeof(ChainingPropertyDescriptor).GetMethod("RemoveValueChanged");
mb = tb.DefineMethod(baseMethod.Name, MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.SpecialName, baseMethod.CallingConvention, baseMethod.ReturnType, new Type[] { typeof(object), typeof(EventHandler) });
il = mb.GetILGenerator();
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Castclass, property.DeclaringType);
il.Emit(OpCodes.Ldarg_2);
il.Emit(OpCodes.Callvirt, ei.GetRemoveMethod());
il.Emit(OpCodes.Ret);
tb.DefineMethodOverride(mb, baseMethod);
}
}
}
PropertyDescriptor newDesc = tb.CreateType().GetConstructor(new Type[] { typeof(PropertyDescriptor) }).Invoke(new object[] { descriptor }) as PropertyDescriptor;
if (newDesc == null)
{
return false;
}
descriptor = newDesc;
s_propertyDescriptors.Add(property, descriptor);
return true;
}
}
catch (Exception ex)
{
return false;
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Security.Permissions;
namespace ConsoleApplication1.HyperDescriptor
{
public class HyperTypeDescriptionProvider : TypeDescriptionProvider
{
private static readonly IDictionary
s_descriptors = new Dictionary();
public HyperTypeDescriptionProvider()
: this(typeof(object))
{
}
public HyperTypeDescriptionProvider(Type type)
: this(TypeDescriptor.GetProvider(type))
{
}
public HyperTypeDescriptionProvider(TypeDescriptionProvider parent)
: base(parent)
{
}
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
lock (s_descriptors)
{
ICustomTypeDescriptor descriptor;
if (!s_descriptors.TryGetValue(objectType, out descriptor))
{
try
{
descriptor = BuildDescriptor(objectType);
}
catch (Exception)
{
return base.GetTypeDescriptor(objectType, instance);
}
}
return descriptor;
}
}
//[ReflectionPermission(SecurityAction.Assert, Flags = ReflectionPermissionFlag.AllFlags)]
[ReflectionPermission(SecurityAction.Assert, Unrestricted=true)]
private ICustomTypeDescriptor BuildDescriptor(Type objectType)
{
// NOTE: "descriptors" already locked here
// get the parent descriptor and add to the dictionary so that
// building the new descriptor will use the base rather than recursing
ICustomTypeDescriptor descriptor = base.GetTypeDescriptor(objectType, null);
s_descriptors.Add(objectType, descriptor);
try
{
// build a new descriptor from this, and replace the lookup
descriptor = new HyperTypeDescriptor(descriptor);
s_descriptors[objectType] = descriptor;
return descriptor;
}
catch
{ // rollback and throw
// (perhaps because the specific caller lacked permissions;
// another caller may be successful)
s_descriptors.Remove(objectType);
throw;
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace ConsoleApplication1.HyperDescriptor
{
public class PathPropertyDescriptor : PropertyDescriptor
{
private readonly PropertyDescriptor m_propertry;
private readonly PropertyDescriptor m_root;
private PropertyDescriptor pd;
private object p;
public PathPropertyDescriptor(PropertyDescriptor root, PropertyDescriptor property)
: base(string.Format("{0}.{1}", root.Name, property.Name), null)
{
m_propertry = property;
m_root = root;
}
public override bool CanResetValue(object component)
{
return false;
}
public override Type ComponentType
{
get
{
return m_propertry.ComponentType;
}
}
public override bool IsReadOnly
{
get
{
return m_propertry.IsReadOnly;
}
}
public override Type PropertyType
{
get {
return m_propertry.PropertyType;
}
}
public override string Description
{
get
{
return m_propertry.Description;
}
}
public override string DisplayName
{
get
{
return m_propertry.DisplayName;
}
}
public override object GetValue(object component)
{
var root = m_root.GetValue(component);
return root == null ? root : m_propertry.GetValue(root);
}
public override void SetValue(object component, object value)
{
var root = m_root.GetValue(component);
if (root != null)
m_propertry.SetValue(root, value);
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
public override void ResetValue(object component)
{
//do nothing
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace ConsoleApplication1.HyperDescriptor
{
public abstract class ChainingPropertyDescriptor : PropertyDescriptor
{
private readonly PropertyDescriptor m_root;
protected ChainingPropertyDescriptor(PropertyDescriptor root)
: base(root)
{
m_root = root;
}
protected PropertyDescriptor Root
{
get {
return m_root;
}
}
public override AttributeCollection Attributes
{
get
{
return m_root.Attributes;
}
}
public override string Category
{
get
{
return m_root.Category;
}
}
public override Type ComponentType
{
get {
return m_root.ComponentType;
}
}
public override TypeConverter Converter
{
get
{
return Root.Converter;
}
}
public override string Description
{
get
{
return m_root.Description;
}
}
public override bool DesignTimeOnly
{
get
{
return m_root.DesignTimeOnly;
}
}
public override string DisplayName
{
get
{
return m_root.DisplayName;
}
}
public override bool IsBrowsable
{
get
{
return m_root.IsBrowsable;
}
}
public override bool IsLocalizable
{
get
{
return m_root.IsLocalizable;
}
}
public override bool IsReadOnly
{
get {
return m_root.IsReadOnly;
}
}
public override string Name
{
get
{
return m_root.Name;
}
}
public override Type PropertyType
{
get {
return m_root.PropertyType;
}
}
public override bool SupportsChangeEvents
{
get
{
return m_root.SupportsChangeEvents;
}
}
public override void AddValueChanged(object component, EventHandler handler)
{
m_root.AddValueChanged(component, handler);
}
public override bool CanResetValue(object component)
{
return m_root.CanResetValue(component);
}
public override bool Equals(object obj)
{
return m_root.Equals(obj);
}
public override PropertyDescriptorCollection GetChildProperties(object instance, Attribute[] filter)
{
return m_root.GetChildProperties(instance, filter);
}
public override object GetEditor(Type editorBaseType)
{
return m_root.GetEditor(editorBaseType);
}
public override int GetHashCode()
{
return m_root.GetHashCode();
}
public override object GetValue(object component)
{
return m_root.GetValue(component);
}
public override void RemoveValueChanged(object component, EventHandler handler)
{
m_root.RemoveValueChanged(component, handler);
}
public override void ResetValue(object component)
{
m_root.ResetValue(component);
}
public override void SetValue(object component, object value)
{
m_root.SetValue(component, value);
}
public override bool ShouldSerializeValue(object component)
{
return m_root.ShouldSerializeValue(component);
}
public override string ToString()
{
return m_root.ToString();
}
}
}