C# 使用propertyBuilder在运行时将属性添加到现有对象

C# 使用propertyBuilder在运行时将属性添加到现有对象,c#,dynamic,reflection,.net-4.0,reflection.emit,C#,Dynamic,Reflection,.net 4.0,Reflection.emit,对象有一些属性,现在是在运行时——当满足条件时我想向该对象添加新属性。 “DynamicObject”不能被证明是正确的,因为我事先不知道属性名 我来到了AcrosPropertyBuilder 但是我找不到有关如何使用propertBuilder向已定义的现有类的现有对象添加属性的帮助。在运行时无法向对象或类型添加真实(反射)属性 如果这里的上下文是数据绑定,那么您可以通过实现一个或多个ICustomTypeDescriptor,TypeDescriptionProvider,TypeConv

对象有一些属性,现在是在运行时——当满足条件时我想向该对象添加新属性。

“DynamicObject”不能被证明是正确的,因为我事先不知道属性名

我来到了AcrosPropertyBuilder

但是我找不到有关如何使用propertBuilder向已定义的现有类的现有对象添加属性的帮助。

在运行时无法向对象或类型添加真实(反射)属性

如果这里的上下文是数据绑定,那么您可以通过实现一个或多个
ICustomTypeDescriptor
TypeDescriptionProvider
TypeConverter
ITypedList
,并为额外属性提供您自己的
PropertyDescriptor
,来实现所有人工属性

  • ICustomTypeDescriptor
    为每个对象,并绑定到该对象
  • TypeDescriptionProvider
    是按对象或按类型提供的,并且与对象分开
  • TypeConverter
    是每种类型的转换器,尤其由
    PropertyGrid
  • ITypedList
    由列表(
    IList
    )用于描述子对象的属性
例如:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        FooConverter.AddProperty("Time", typeof(DateTime));
        FooConverter.AddProperty("Age", typeof(int));
        using (var grid = new PropertyGrid { Dock = DockStyle.Fill, SelectedObject = new Foo() })
        using (var form = new Form { Controls = { grid } })
        {
            Application.Run(form);
        }
    }
}
class FooConverter : ExpandableObjectConverter
{
    private static readonly List<Tuple<string, Type>> customProps = new List<Tuple<string, Type>>();
    public static void AddProperty(string name, Type type)
    {
        lock (customProps) customProps.Add(Tuple.Create(name, type));
    }
    public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, System.Attribute[] attributes)
    {
        var orig = base.GetProperties(context, value, attributes);
        lock(customProps)
        {
            if(customProps.Count == 0) return orig;

            PropertyDescriptor[] props = new PropertyDescriptor[orig.Count + customProps.Count];
            orig.CopyTo(props, 0);
            int i = orig.Count;
            foreach (var prop in customProps)
            {
                props[i++] = new SimpleDescriptor(prop.Item1, prop.Item2);
            }
            return new PropertyDescriptorCollection(props);
        }
    }
    class SimpleDescriptor : PropertyDescriptor
    {
        private readonly Type type;
        public SimpleDescriptor(string name, Type type)
            : base(name, new Attribute[0])
        {
            this.type = type;
        }
        public override Type PropertyType { get { return type;} }
        public override bool SupportsChangeEvents { get { return false; } }
        public override void ResetValue(object component) { SetValue(component, null); }
        public override bool CanResetValue(object component) { return true; }
        public override bool ShouldSerializeValue(object component) { return GetValue(component) != null; }
        public override bool IsReadOnly { get { return false; } }
        public override Type ComponentType { get { return typeof(Foo); } }
        public override object GetValue(object component) { return ((Foo)component).GetExtraValue(Name); }
        public override void SetValue(object component, object value) { ((Foo)component).SetExtraValue(Name, value); }
        public override string Category { get { return "Extra values"; } }
    }
}
[TypeConverter(typeof(FooConverter))]
public class Foo
{
    Dictionary<string, object> extraValues;
    internal object GetExtraValue(string name)
    {
        object value;
        if (extraValues == null || !extraValues.TryGetValue(name, out value)) value = null;
        return value;
    }
    internal void SetExtraValue(string name, object value)
    {
        if (extraValues == null && value != null) extraValues = new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase);
        if (value == null) extraValues.Remove(name);
        else extraValues[name] = value;
    }
    public int Id { get; set; }
    public string Name { get; set; }
}
使用系统;
使用System.Collections.Generic;
使用系统组件模型;
使用System.Windows.Forms;
静态类程序
{
[状态线程]
静态void Main()
{
Application.EnableVisualStyles();
AddProperty(“时间”,typeof(DateTime));
AddProperty(“年龄”,typeof(int));
使用(var grid=new PropertyGrid{Dock=DockStyle.Fill,SelectedObject=new Foo()})
使用(var form=newform{Controls={grid}})
{
申请表格;
}
}
}
类FooConverter:ExpandableObjectConverter
{
私有静态只读列表customProps=new List();
公共静态void AddProperty(字符串名称、类型)
{
lock(customProps)customProps.Add(Tuple.Create(name,type));
}
public override PropertyDescriptorCollection GetProperties(ITypeScriptorContext上下文、对象值、System.Attribute[]属性)
{
var orig=base.GetProperties(上下文、值、属性);
锁(定制道具)
{
如果(customProps.Count==0)返回原点;
PropertyDescriptor[]props=新的PropertyDescriptor[orig.Count+customProps.Count];
原始复制到(道具,0);
int i=原始计数;
foreach(customProps中的var prop)
{
props[i++]=新的SimpleDescriptor(prop.Item1,prop.Item2);
}
返回新的PropertyDescriptorCollection(道具);
}
}
类SimpleDescriptor:PropertyDescriptor
{
私有只读类型;
公共SimpleDescriptor(字符串名称、类型)
:base(名称,新属性[0])
{
this.type=type;
}
公共重写类型PropertyType{get{return Type;}}
public override bool SupportsChangeEvents{get{return false;}}
public override void ResetValue(对象组件){SetValue(组件,null);}
公共重写bool CanResetValue(对象组件){return true;}
公共重写bool ShouldSerializeValue(对象组件){return GetValue(组件)!=null;}
公共重写bool IsReadOnly{get{return false;}}
公共重写类型ComponentType{get{return typeof(Foo);}
公共重写对象GetValue(对象组件){return((Foo)组件).GetExtraValue(名称);}
公共重写无效SetValue(对象组件,对象值){((Foo)组件).SetExtraValue(名称,值);}
公共重写字符串类别{get{返回“额外值”;}
}
}
[TypeConverter(typeof(FooConverter))]
公开课Foo
{
字典附加值;
内部对象GetExtraValue(字符串名称)
{
目标价值;
如果(extraValues==null | | |!extraValues.TryGetValue(name,out value))value=null;
返回值;
}
内部void SetExtraValue(字符串名称、对象值)
{
if(extraValues==null&&value!=null)extraValues=newdictionary(StringComparer.InvariantCultureIgnoreCase);
if(value==null)extraValues.Remove(name);
else extraValues[名称]=值;
}
公共int Id{get;set;}
公共字符串名称{get;set;}
}
检查此地址


@KhannaB7如果您在
PropertyGrid
中显示它,那么
TypeConverter
是最简单的选项-远比实现
ICustomTypeDescriptor
TypeDescriptionProvider
容易-您只需子类化
ExpandableObjectConverter
并覆盖
GetProperties
,并使用
TypeConverterAttribute
将转换器链接到您的类型。Grawell♦ 我想要做的是:向该对象添加一个属性(分配一个属性-自定义UIEditor),在propertyGrid中显示该属性,对其进行编辑,甚至获取该属性的编辑值,用于其他操作。。可能吗???是的。。。照我说的做above@KhannaB7完整的示例addedur解决方案非常棒。谢谢你的帮助。
using System;
using System.Threading;
using System.Reflection;
using System.Reflection.Emit;

class EmitWriteLineDemo {

   public static Type CreateDynamicType() {       
       Type[] ctorParams = new Type[] {typeof(int),
                   typeof(int)};

       AppDomain myDomain = Thread.GetDomain();
       AssemblyName myAsmName = new AssemblyName();
       myAsmName.Name = "MyDynamicAssembly";

       AssemblyBuilder myAsmBuilder = myDomain.DefineDynamicAssembly(
                      myAsmName, 
                      AssemblyBuilderAccess.Run);

       ModuleBuilder pointModule = myAsmBuilder.DefineDynamicModule("PointModule",
                                    "Point.dll");

       TypeBuilder pointTypeBld = pointModule.DefineType("Point",
                                  TypeAttributes.Public);

       FieldBuilder xField = pointTypeBld.DefineField("x", typeof(int),
                                                      FieldAttributes.Public);
       FieldBuilder yField = pointTypeBld.DefineField("y", typeof(int), 
                                                      FieldAttributes.Public);


       Type objType = Type.GetType("System.Object"); 
       ConstructorInfo objCtor = objType.GetConstructor(new Type[0]);

       ConstructorBuilder pointCtor = pointTypeBld.DefineConstructor(
                                   MethodAttributes.Public,
                                   CallingConventions.Standard,
                                   ctorParams);
       ILGenerator ctorIL = pointCtor.GetILGenerator();


       // First, you build the constructor.
       ctorIL.Emit(OpCodes.Ldarg_0);
       ctorIL.Emit(OpCodes.Call, objCtor);
       ctorIL.Emit(OpCodes.Ldarg_0);
       ctorIL.Emit(OpCodes.Ldarg_1);
       ctorIL.Emit(OpCodes.Stfld, xField); 
       ctorIL.Emit(OpCodes.Ldarg_0);
       ctorIL.Emit(OpCodes.Ldarg_2);
       ctorIL.Emit(OpCodes.Stfld, yField); 
       ctorIL.Emit(OpCodes.Ret); 

       //  Now, you'll build a method to output some information on the
       // inside your dynamic class. This method will have the following
       // definition in C#:
    //  public void WritePoint()

       MethodBuilder writeStrMthd = pointTypeBld.DefineMethod(
                                     "WritePoint", 
                             MethodAttributes.Public,
                                             typeof(void), 
                                             null);


       ILGenerator writeStrIL = writeStrMthd.GetILGenerator();

       // The below ILGenerator created demonstrates a few ways to create
       // string output through STDIN. 

       // ILGenerator.EmitWriteLine(string) will generate a ldstr and a 
       // call to WriteLine for you.

       writeStrIL.EmitWriteLine("The value of this current instance is:");

       // Here, you will do the hard work yourself. First, you need to create
       // the string we will be passing and obtain the correct WriteLine overload
       // for said string. In the below case, you are substituting in two values,
       // so the chosen overload is Console.WriteLine(string, object, object).

       String inStr = "({0}, {1})";
       Type[] wlParams = new Type[] {typeof(string),
                     typeof(object),
                     typeof(object)};

       // We need the MethodInfo to pass into EmitCall later.

       MethodInfo writeLineMI = typeof(Console).GetMethod(
                            "WriteLine",
                        wlParams);

       // Push the string with the substitutions onto the stack.
       // This is the first argument for WriteLine - the string one. 

       writeStrIL.Emit(OpCodes.Ldstr, inStr);

       // Since the second argument is an object, and it corresponds to
       // to the substitution for the value of our integer field, you 
       // need to box that field to an object. First, push a reference
       // to the current instance, and then push the value stored in
       // field 'x'. We need the reference to the current instance (stored
       // in local argument index 0) so Ldfld can load from the correct
       // instance (this one).

       writeStrIL.Emit(OpCodes.Ldarg_0);
       writeStrIL.Emit(OpCodes.Ldfld, xField);

       // Now, we execute the box opcode, which pops the value of field 'x',
       // returning a reference to the integer value boxed as an object.

       writeStrIL.Emit(OpCodes.Box, typeof(int));

       // Atop the stack, you'll find our string inStr, followed by a reference
       // to the boxed value of 'x'. Now, you need to likewise box field 'y'.

       writeStrIL.Emit(OpCodes.Ldarg_0);
       writeStrIL.Emit(OpCodes.Ldfld, yField);
       writeStrIL.Emit(OpCodes.Box, typeof(int));

       // Now, you have all of the arguments for your call to
       // Console.WriteLine(string, object, object) atop the stack:
       // the string InStr, a reference to the boxed value of 'x', and
       // a reference to the boxed value of 'y'.

       // Call Console.WriteLine(string, object, object) with EmitCall.

       writeStrIL.EmitCall(OpCodes.Call, writeLineMI, null);

       // Lastly, EmitWriteLine can also output the value of a field
       // using the overload EmitWriteLine(FieldInfo).

       writeStrIL.EmitWriteLine("The value of 'x' is:");
       writeStrIL.EmitWriteLine(xField);
       writeStrIL.EmitWriteLine("The value of 'y' is:");
       writeStrIL.EmitWriteLine(yField);

       // Since we return no value (void), the the ret opcode will not
       // return the top stack value.

       writeStrIL.Emit(OpCodes.Ret);

       return pointTypeBld.CreateType();

   }

   public static void Main() {

      object[] ctorParams = new object[2];

      Console.Write("Enter a integer value for X: "); 
      string myX = Console.ReadLine();
      Console.Write("Enter a integer value for Y: "); 
      string myY = Console.ReadLine();

      Console.WriteLine("---");

      ctorParams[0] = Convert.ToInt32(myX);
      ctorParams[1] = Convert.ToInt32(myY);

      Type ptType = CreateDynamicType();

      object ptInstance = Activator.CreateInstance(ptType, ctorParams);
      ptType.InvokeMember("WritePoint",
              BindingFlags.InvokeMethod,
              null,
              ptInstance,
              new object[0]);
   }
}