Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create new PropertyInfo object on the fly

This is my very first post, and although I have searched in topics related to my issue to some extent, I'm having a lot of trouble finding the proper answer.

My question may be very simple, but I'm aware that the answer might not be so easy to give. If any exists at all.

With that being said, this is my case: as an example, I have an array of PropertyInfo objects, which I am using to get the properties from a class, like this:

public PropertyInfo[] GetProperties (object o)
{
    PropertyInfo[] properties = o.GetType().GetProperties();

    return properties;
}

Looks easy, right? Now my problem is this: how to create a new PropertyInfo object and add it to the array?

I have seen other posts where users want to set the VALUE of a PropertyInfo, but that is not what I need. What I need is to create on the fly a new PropertyInfo object, where the only available data I have is the Name and the Type.

The test case I posted earlier is merely a small example of what I am trying to achieve. My true and final goal is, in fact, to be able to create a new PropertyInfo based on this class:

public class DynamicClass
{
    public Type ObjectType { get; set; }
    public List<string> PropertyNameList { get; set; }
    public List<Type> PropertyTypeList { get; set; }
}

I hope someone can help me achieve this. Many thanks in advance!

EDITED: I forgot to add o.GetType() before GetProperties() method. Thanks Ilya Ivanov!

I am calling the method SelectProperties like so:

list = _queriable.Select(SelectProperties).ToList();

The method looks like this:

private Expression<Func<T, List<string>>> SelectProperties
{
    get
    {
       return value => _properties.Select
                      (
                          prop => (prop.GetValue(value, new object[0]) ?? string.Empty).ToString()
                      ).ToList();
        }
    }

Best regards,

Luis


UPDATE:

Ok, so I am following 280Z28's advice and I am inheriting PropertyInfo in a new class. I've done more research and I found in MSDN that I need to override the following methods: GetValue, SetValue, GetAccessors, GetGetMethod, GetSetMethod, and GetIndexParameters.

However, when I try to call base with the parameters it gives me error saying and I quote "Cannot call an abstract member: 'System.Reflection.PropertyInfo.GetAccessesors(bool)'". If I try to call the method without any parameters, it does not show up any error but I feel like that is the wrong approach.

This is what I've got so far:

public override MethodInfo[] GetAccessors(bool nonPublic)
{
   MethodInfo[] temp = base.GetAccessors(nonPublic);

return temp;
}

UPDATE 2:

Ok, That did not work well. After some hours of trying to do derived class of either PropertyInfo or PropertyDescriptor, I have decided to not go through with that approach.

Instead, I had another idea from reading other posts. My true problem lies in the fact that the class I usually read and use to get the properties is not always the same. So I realized what I probably really need is just a way to create a dynamic class on the fly, and only then get the properties.

I read that there is such a thing called ExpandoObject and ElasticObject, although I don't quite yet know how to apply them to my problem in order to get finally a solution.

Ok now, what I really AM doing is this -> I have been using the solution mentioned in the following link: jQuery DataTables Plugin Meets C#.

The thing is, This assumes I will have different static models/classes for each DB table. However in my case, I will have two types of columns: The ones provided by each DB table class (aka basic columns) and then additional columns that I am dynamically supplying already in my adaptation.

For example: if this the DB table class:

public class Table1
{
    public int Field1;
    public string Field2;
    public string Field3;
}

And then I supply an extra column called "Action" of type string, then In the DataTableParser class, in the _properties attribure there should be the following information:

_properties[0] should be int32 Field1
_properties[1] should be String Field2
_properties[2] should be String Field3
_properties[3] should be String Action

And to be honest that is ALL I need! Nothing more, nothing less! The rest I am already parsing!

In the end, because I have a different number of columns (supplied) than the object passed to the DataTableParser class, it always gives error during Sorting and Filtering the DataTable.

Any help please? I really need it! Thanks again.

Best regards,

Luis

like image 230
MadGatsu Avatar asked Mar 26 '13 15:03

MadGatsu


2 Answers

Solution:

Ok, basically what I've done was reusing the MyTypeBuilder class (with some adaptations) from the topic below in order to create a dynamic object and added a method to get an IList from it.

Link: How to dynamically create a class in C#?

public class MyObjectBuilder
{
    public Type objType { get; set; }

    public MyObjectBuilder()
    {
        this.objType = null;
    }

    public object CreateNewObject(List<Field> Fields)
    {
        this.objType = CompileResultType(Fields);
        var myObject = Activator.CreateInstance(this.objType);

        return myObject;
    }

    public IList getObjectList()
    {
        Type listType = typeof(List<>).MakeGenericType(this.objType);

        return (IList)Activator.CreateInstance(listType);
    }

    public static MethodInfo GetCompareToMethod(object genericInstance, string sortExpression)
    {
        Type genericType = genericInstance.GetType();
        object sortExpressionValue = genericType.GetProperty(sortExpression).GetValue(genericInstance, null);
        Type sortExpressionType = sortExpressionValue.GetType();
        MethodInfo compareToMethodOfSortExpressionType = sortExpressionType.GetMethod("CompareTo", new Type[] { sortExpressionType });

        return compareToMethodOfSortExpressionType;
    }

    public static Type CompileResultType(List<Field> Fields)
    {
        TypeBuilder tb = GetTypeBuilder();
        ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

        // NOTE: assuming your list contains Field objects with fields FieldName(string) and FieldType(Type)
        foreach (var field in Fields)
            CreateProperty(tb, field.FieldName, field.FieldType);

        Type objectType = tb.CreateType();
        return objectType;
    }

    private static TypeBuilder GetTypeBuilder()
    {
        var typeSignature = "MyDynamicType";
        var an = new AssemblyName(typeSignature);
        AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
        ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
        TypeBuilder tb = moduleBuilder.DefineType(typeSignature
                            , TypeAttributes.Public |
                            TypeAttributes.Class |
                            TypeAttributes.AutoClass |
                            TypeAttributes.AnsiClass |
                            TypeAttributes.BeforeFieldInit |
                            TypeAttributes.AutoLayout
                            , null);
        return tb;
    }

    private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
    {
        FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);

        PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
        MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
        ILGenerator getIl = getPropMthdBldr.GetILGenerator();

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

        MethodBuilder setPropMthdBldr =
            tb.DefineMethod("set_" + propertyName,
              MethodAttributes.Public |
              MethodAttributes.SpecialName |
              MethodAttributes.HideBySig,
              null, new[] { propertyType });

        ILGenerator setIl = setPropMthdBldr.GetILGenerator();
        Label modifyProperty = setIl.DefineLabel();
        Label exitSet = setIl.DefineLabel();

        setIl.MarkLabel(modifyProperty);
        setIl.Emit(OpCodes.Ldarg_0);
        setIl.Emit(OpCodes.Ldarg_1);
        setIl.Emit(OpCodes.Stfld, fieldBuilder);

        setIl.Emit(OpCodes.Nop);
        setIl.MarkLabel(exitSet);
        setIl.Emit(OpCodes.Ret);

        propertyBuilder.SetGetMethod(getPropMthdBldr);
        propertyBuilder.SetSetMethod(setPropMthdBldr);
    }
}

Also, I used a Field class like so to have information on a particular Field

public class Field
{
   public string FieldName;
   public Type FieldType;
}

Finally, I found this method somewhere that allows a person to set the value to a Member:

public static void SetMemberValue(MemberInfo member, object target, object value)
{
   switch (member.MemberType)
   {
       case MemberTypes.Field:
           ((FieldInfo)member).SetValue(target, value);
           break;
       case MemberTypes.Property:
           ((PropertyInfo)member).SetValue(target, value, null);
           break;
       default:
           throw new ArgumentException("MemberInfo must be if type FieldInfo or PropertyInfo", "member");
   }
}

Afterwards, I did the following to create a dynamic object and insert a property on it:

//Creating a List of Fields (string FieldName, Type FieldType) 
List<Field> Fields = new List<Field>();
Fields.Add(new Field { FieldName = "TestName", FieldType = typeof(string) });

//MyObjectBuilder Class
MyObjectBuilder o = new MyObjectBuilder();

//Creating a new object dynamically
object newObj = o.CreateNewObject(Fields);
IList objList = o.getObjectList();

Type t = newObj.GetType();
object instance = Activator.CreateInstance(t);

PropertyInfo[] props = instance.GetType().GetProperties();

int instancePropsCount = props.Count();

for (int i = 0; i < instancePropsCount; ++i)
{
   string fieldName = props[i].Name;
   MemberInfo[] mInfo = null;
   PropertyInfo pInfo = newObj.GetType().GetProperty(fieldName);

   if (pInfo != null)
   {
       var value = pInfo.GetValue(newObj, null);
       mInfo = t.GetMember(fieldName);

       if (value != null && mInfo != null && !string.IsNullOrEmpty(mInfo[0].ToString()))
           SetMemberValue(mInfo[0], instance, value);
   }
   else
   {
       mInfo = t.GetMember(fieldName);

       if (mInfo != null && !string.IsNullOrEmpty(mInfo[0].ToString()))
           SetMemberValue(mInfo[0], instance, null);
   }
}

objList.Add(instance);

This solution goes a little bit beyond my initial question, but it does show how to create an object dynamically, hence allowing us to add properties on the fly to that object.

like image 131
MadGatsu Avatar answered Sep 21 '22 14:09

MadGatsu


PropertyInfo is an abstract class. If you only need the Name and PropertyType properties, you can derive your own class from it to provide this information.

The places where you could make use of these instances is extremely restricted. To help you better we will need to know exactly how you plan to make use of the PropertyInfo objects you're trying to create.

Edit: Based on the edit, you may be able to use this class as long as you implement the GetValue method in some manner specific to the objects you are creating. It's not clear what you are doing with the DynamicClass instances, or more importantly where the property values are supposed to be stored.

public class SimplePropertyInfo : PropertyInfo
{
    private readonly string _name;
    private readonly Type _propertyType;

    public SimplePropertyInfo(string name, Type propertyType)
    {
        if (name == null)
            throw new ArgumentNullException("name");
        if (propertyType == null)
            throw new ArgumentNullException("propertyType");
        if (string.IsNullOrEmpty(name))
            throw new ArgumentException("name");

        _name = name;
        _propertyType = propertyType;
    }

    public override string Name
    {
        get
        {
            return _name;
        }
    }

    public override Type PropertyType
    {
        get
        {
            return _propertyType;
        }
    }

    public override PropertyAttributes Attributes
    {
        get
        {
            throw new NotImplementedException();
        }
    }

    public override bool CanRead
    {
        get
        {
            throw new NotImplementedException();
        }
    }

    public override bool CanWrite
    {
        get
        {
            throw new NotImplementedException();
        }
    }

    public override Type DeclaringType
    {
        get
        {
            throw new NotImplementedException();
        }
    }

    public override Type ReflectedType
    {
        get
        {
            throw new NotImplementedException();
        }
    }

    public override MethodInfo[] GetAccessors(bool nonPublic)
    {
        throw new NotImplementedException();
    }

    public override MethodInfo GetGetMethod(bool nonPublic)
    {
        throw new NotImplementedException();
    }

    public override ParameterInfo[] GetIndexParameters()
    {
        throw new NotImplementedException();
    }

    public override MethodInfo GetSetMethod(bool nonPublic)
    {
        throw new NotImplementedException();
    }

    public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    public override object[] GetCustomAttributes(Type attributeType, bool inherit)
    {
        throw new NotImplementedException();
    }

    public override object[] GetCustomAttributes(bool inherit)
    {
        throw new NotImplementedException();
    }

    public override bool IsDefined(Type attributeType, bool inherit)
    {
        throw new NotImplementedException();
    }
}
like image 29
Sam Harwell Avatar answered Sep 21 '22 14:09

Sam Harwell