动态创建新的PropertyInfo对象

时间:2013-03-26 15:45:26

标签: c# object reflection propertyinfo

这是我的第一篇文章,虽然我在某种程度上搜索了与我的问题相关的主题,但我在找到正确的答案方面遇到了很多麻烦。

我的问题可能很简单,但我知道答案可能不那么容易。如果存在的话。

话虽如此,这是我的情况:作为一个例子,我有一个PropertyInfo对象数组,我用来从类中获取属性,如下所示:

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

    return properties;
}

看起来很简单吧?现在我的问题是:如何创建一个新的PropertyInfo对象并将其添加到数组中?

我看过其他帖子,用户想要设置PropertyInfo的VALUE,但这不是我需要的。我需要的是动态创建一个新的PropertyInfo对象,其中我唯一可用的数据是名称类型

我之前发布的测试用例只是我想要实现的一个小例子。 事实上,我真正的最终目标是能够基于这个类创建一个新的PropertyInfo:

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

我希望有人可以帮助我实现这一目标。非常感谢提前!

已编辑:我忘了在GetProperties()方法之前添加o.GetType()。谢谢Ilya Ivanov!

我正在调用SelectProperties方法,如下所示:

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

方法如下:

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

致以最诚挚的问候,

路易斯


更新

好的,所以我遵循280Z28的建议,我在一个新类中继承PropertyInfo。我做了更多的研究,我在MSDN中发现我需要覆盖以下方法: GetValue,SetValue,GetAccessors,GetGetMethod,GetSetMethod和GetIndexParameters。

但是,当我尝试使用参数调用base时,它会给出错误说法,并引用“无法调用抽象成员:'System.Reflection.PropertyInfo.GetAccessesors(bool)'”。如果我尝试在没有任何参数的情况下调用该方法,它不会显示任何错误,但我觉得这是错误的方法。

这是我到目前为止所得到的:

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

return temp;
}

更新2

好的,那不行。在尝试使用PropertyInfo或PropertyDescriptor的派生类几个小时后,我决定不采用这种方法。

相反,我从阅读其他帖子中得到了另一个想法。我真正的问题在于我通常阅读并用于获取属性的类并不总是相同的。所以我意识到我可能真正需要的只是一种动态创建动态类的方法,只有然后才能获得属性。

我读到有一个名为ExpandoObject和ElasticObject的东西,虽然我还不知道如何将它们应用到我的问题中以便最终获得解决方案。

好的,我现在正在做的是这个 - &gt;我一直在使用以下链接中提到的解决方案:jQuery DataTables Plugin Meets C#.

问题是,假设我将为每个数据库表提供不同的静态模型/类。但是在我的例子中,我将有两种类型的列:每个DB表类提供的列(也就是基本列),然后是我在我的改编中动态提供的其他列。

例如:如果这是DB表类:

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

然后我提供了一个名为“Action”的额外列,类型为string ,然后在DataTableParser类中,在_properties attribure中应该有以下信息:

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

说实话,我需要 ALL !没有更多,没有更少!剩下的我已经解析了!

最后,因为我提供的列数(提供的)与传递给DataTableParser类的对象不同,所以在排序和过滤DataTable期间总是会出错。

请帮忙吗?我真的需要它!再次感谢。

致以最诚挚的问候,

路易斯

4 个答案:

答案 0 :(得分:10)

<强>

解决方案:

好的,基本上我所做的就是从下面的主题重用MyTypeBuilder类(带一些改编),以便创建一个动态对象并添加一个方法来从中获取IList。

链接: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);
    }
}

另外,我使用类似的Field类来获取特定Field

的信息
public class Field
{
   public string FieldName;
   public Type FieldType;
}

最后,我发现这个方法允许某人将值设置为成员:

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");
   }
}

之后,我执行了以下操作来创建动态对象并在其上插入属性:

//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);

这个解决方案稍微超出了我的初始问题,但它确实展示了如何动态创建对象,因此允许我们动态地向该对象添加属性。

答案 1 :(得分:6)

PropertyInfo是一个抽象类。如果您只需要NamePropertyType属性,则可以从中派生自己的类以提供此信息。

您可以使用这些实例的地方受到极大限制。为了更好地帮助您,我们需要知道完全您打算如何使用{ {1}}您要创建的对象。

编辑:根据编辑,您可以使用此类,只要您以特定于您正在创建的对象的某种方式实施PropertyInfo方法即可。目前还不清楚你在使用GetValue实例做什么,或者更重要的是应该存储属性值的位置。

DynamicClass

答案 2 :(得分:0)

由于PropertyInfo是一个抽象类,您必须自己实现它:

class MyPropertyInfo : PropertyInfo
{
}

您需要覆盖一大堆方法和属性,但由于您只需查询NameType,因此您可以简单地throw new InvalidOperationException()查看其他方法和属性。

由于数组是固定大小的,因此无法将假PropertyInfo添加到数组中,但您可以创建List<PropertyInfo>,将原始数组和假PropertyInfo添加到数组中它,并返回theList.ToArray()

我会在实现之前考虑其他选项,例如创建自己的MyProperty派生自PropertyInfo并返回其中的数组。

答案 3 :(得分:0)

您有Method与其他几个实例具有共性,处理此类事件的最有效方式是通过 Polymorphism

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

然后您可以执行以下操作:

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

等等......

现在你需要明确添加一个如何填充这些项目的实现;但是你可以打电话:

DynamicClass[] obj = new DynamicClass[]

obj[0] = new Something();
obj[1] = new SomethingElse();
obj[2] = new AndAnotherSomething();

这是一种非常非常宽松的实现方式。但它可以让你像你想要的那样实现这些领域;然后将它们填入数组中。为了你处理。

我可能误解了你的问题,但我可能会建议多态性