对象有一些属性,现在在运行时 - 满足条件时.. 我想为此对象添加新属性。
“DynamicObject”无法使用,因为我事先不知道属性名称。
我来了 PropertyBuilder http://msdn.microsoft.com/en-us/library/system.reflection.emit.propertybuilder.aspx
但我无法找到有关如何使用propertiesBuilder为已定义现有类的现有对象添加属性的帮助。
答案 0 :(得分:4)
您无法在运行时向对象或类型添加真实(反射)属性。
如果此处的上下文是数据绑定,那么您可以通过实施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; }
}
答案 1 :(得分:1)
检查此地址 https://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes(v=vs.110).aspx
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]);
}
}