none
Use reflection to dynamically add properties to object at runtime?

    Question

  • I have a class that exists at runtime that I would like to dynamically add properties to. I dont know the name of the properties until runtime and the properties will all point to objects with the name of the object as the name of the property.

     

    ie:

    Code Snippet

     

    //my object to dynamically add properties to

    scene = new Scene()

     

    //my object to access via property from the above object

    childElement = new ChildElement()

    childElement.Name = "child1"

     

    //how I want to be able to access it via the scene object

    scene.child1.Colour = Red

     

     

    Any ideas? I thought maybe reflection can help somehow?

     

    Wednesday, January 30, 2008 3:01 AM

Answers

  • Looks like you are compiling C# code dynamically. So far as I know, this is not possible for C# language.

    Even you can compile C# code using CodeDOM or Reflection.Emit at runtime, for the compiled type or class, you cannot modify it's members. This is a big difference between static language and dynamic language.
    Thursday, January 31, 2008 7:11 AM

All replies

  • Wednesday, January 30, 2008 3:40 AM
  • What would be the point to add these properties at runtime? You won't be able to access these properties in the way you want because the class is not know to the compiler.

    I've probably missed something, but what about using interfaces? Since you would like to set a property on a child object, I asume that you know which properties are available to the child (although the child might not always be of the same class).
    Wednesday, January 30, 2008 4:15 AM
  •  Gregor Berginc wrote:
    What would be the point to add these properties at runtime? You won't be able to access these properties in the way you want because the class is not know to the compiler.

    The child class is already known to the compiler, I would just like to reference them via the scene.childname syntax. 

    A little more detail:

    Currently users must (via script) type something similar to scene.getcontent("nameOfContent") which isn't as elegant as scene.nameOfContent. Due to some other limitations in the current scripting library, scene["nameOfContent"] will not work and neither will scene.content["nameOfContent"] (the name value pair scenario in the first reply), so im left with either the way it is that isnt ideal, or the other way which im hoping is possible, where a property is able to be generated at runtime on the Scene object that points at an existing instance of a ChildElement 

    Wednesday, January 30, 2008 5:21 AM
  • Looks like you are compiling C# code dynamically. So far as I know, this is not possible for C# language.

    Even you can compile C# code using CodeDOM or Reflection.Emit at runtime, for the compiled type or class, you cannot modify it's members. This is a big difference between static language and dynamic language.
    Thursday, January 31, 2008 7:11 AM
  • hi there,

     

    try something like this, it creates an object on fly and adds properties to it

     

       TypeBuilder builder = GetTypeBuilder(jo.GetHashCode());

                ConstructorBuilder constructor = builder.DefineDefaultConstructor(
                    MethodAttributes.Public |
                    MethodAttributes.SpecialName |
                    MethodAttributes.RTSpecialName);

               // add all the properties to the object

               for (int i=0; i <jo.Count; i++)
                {

                    CreateProperty(builder, jo<"proeprty name form jo i">, "".GetType() );
                }

            // make the type

             Type t = builder.CreateType();

           // make insatnce

             var obj = Activator.CreateInstance(t);

    // set a property

      SetProperty(obj, propertyName, value, false);

     

    // helper methods - some of this code was given to me from http://www.telerik.com/ thanks ( i have modified it )

     

     

     public static TypeBuilder GetTypeBuilder(int randomValue)
            {
                AssemblyName an = new AssemblyName("DynamicAssembly" + randomValue.ToString());
                AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
                ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");

                TypeBuilder tb = moduleBuilder.DefineType("DynamicType"
                                    , TypeAttributes.Public |
                                    TypeAttributes.Class |
                                    TypeAttributes.AutoClass |
                                    TypeAttributes.AnsiClass |
                                    TypeAttributes.BeforeFieldInit |
                                    TypeAttributes.AutoLayout
                                    , typeof(object));
                return tb;
            }

            private static void CreateProperty(TypeBuilder builder, string propertyName, Type propertyType)
            {
                FieldBuilder fieldBuilder = builder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
                PropertyBuilder propertyBuilder = builder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);

                MethodBuilder getPropertyBuiler = CreatePropertyGetter(builder, fieldBuilder);
                MethodBuilder setPropertyBuiler = CreatePropertySetter(builder, fieldBuilder);

                propertyBuilder.SetGetMethod(getPropertyBuiler);
                propertyBuilder.SetSetMethod(setPropertyBuiler);
            }

            private static MethodBuilder CreatePropertyGetter(TypeBuilder typeBuilder, FieldBuilder fieldBuilder)
            {
                MethodBuilder getMethodBuilder =
                    typeBuilder.DefineMethod("get_" + fieldBuilder.Name,
                        MethodAttributes.Public |
                        MethodAttributes.SpecialName |
                        MethodAttributes.HideBySig,
                        fieldBuilder.FieldType, Type.EmptyTypes);

                ILGenerator getIL = getMethodBuilder.GetILGenerator();

                getIL.Emit(OpCodes.Ldarg_0);
                getIL.Emit(OpCodes.Ldfld, fieldBuilder);
                getIL.Emit(OpCodes.Ret);

                return getMethodBuilder;
            }

            private static MethodBuilder CreatePropertySetter(TypeBuilder typeBuilder, FieldBuilder fieldBuilder)
            {
                MethodBuilder setMethodBuilder =
                    typeBuilder.DefineMethod("set_" + fieldBuilder.Name,
                      MethodAttributes.Public |
                      MethodAttributes.SpecialName |
                      MethodAttributes.HideBySig,
                      null, new Type[] { fieldBuilder.FieldType });

                ILGenerator setIL = setMethodBuilder.GetILGenerator();

                setIL.Emit(OpCodes.Ldarg_0);
                setIL.Emit(OpCodes.Ldarg_1);
                setIL.Emit(OpCodes.Stfld, fieldBuilder);
                setIL.Emit(OpCodes.Ret);

                return setMethodBuilder;
            }

     

      public static object SetProperty(object Target, string Name, object value, bool ignoreIfTargetIsNull)
            {

                if (ignoreIfTargetIsNull && Target == null) return null;

                object[] values = { value };

                object oldProperty = GetProperty(Target, Name);

                PropertyInfo targetProperty = Target.GetType().GetProperty(Name);

                if (targetProperty == null)
                {
                    throw new Exception("Object " + Target.ToString() + "   does not have Target Property " + Name);

                }


                targetProperty.GetSetMethod().Invoke(Target, values);


                return oldProperty;

            }

     

    public static object GetProperty(object Target, string Name, bool throwError)
            {

                PropertyInfo targetProperty = Target.GetType().GetProperty(Name);

                if (targetProperty == null)
                {
                    // hack for password box as it cannot be subclassed
                    if (Target is PasswordBox)
                    {
                        return ((PasswordBox)Target).ID(); // ID is an extention method
                    }
                    else if (throwError)
                    {
                        throw new Exception("Object " + Target.ToString() + "   does not have Target Property " + Name);
                    }
                    else
                    {
                        return null;
                    }
                }
                else
                {

                    return targetProperty.GetGetMethod().Invoke(Target, null);
                }

            }

     

    //Good Luck

    //Regards Nick

     

     

    Thursday, June 16, 2011 7:13 AM