动态创建新的PropertyInfo对象

本文关键字:PropertyInfo 对象 创建 动态 | 更新日期: 2023-09-27 18:06:01

这是我的第一个帖子,虽然我在某种程度上搜索了与我的问题相关的主题,但我很难找到合适的答案。

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

话是这么说的,这是我的情况:作为一个例子,我有一个PropertyInfo对象数组,我用它来从类中获取属性,像这样:

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

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

我看到其他的帖子,用户想要设置PropertyInfo的值,但这不是我需要的。我需要的是在飞行中创建一个新的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()。谢谢,伊利亚·伊万诺夫!

我像这样调用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();
        }
    }

最诚挚的问候,Luis


:

好的,所以我遵循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,虽然我还不太知道如何将它们应用到我的问题中,以便最终得到一个解决方案。

Ok现在,我真正在做的是这个->我一直在使用以下链接中提到的解决方案:jQuery数据表插件满足c#。

的事情是,这假设我将有不同的静态模型/类为每个DB表。然而,在我的例子中,我将有两种类型的列:由每个DB表类提供的列(又名基本列),然后是我在我的改编中动态提供的附加列。

例如:如果这是DB表类:

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

然后我提供一个额外的列名为"动作"类型字符串,然后在DataTableParser类中,在_properties属性中应该有以下信息:

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

老实说,这是所有我需要的!不多不少!其余的我已经在解析了!

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

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

最诚挚的问候,

Luis

动态创建新的PropertyInfo对象

解决方案:

好的,基本上我所做的是重用MyTypeBuilder类(有一些调整)从下面的主题,以创建一个动态对象,并添加了一个方法来获取列表从它。

链接:如何在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类来获取特定字段

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

最后,我在某个地方找到了这个方法,它允许人们将值设置为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");
   }
}

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

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

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

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

可以使用这些实例的地方是非常有限的。为了更好地帮助您,我们需要确切地知道您计划如何使用您正在尝试创建的PropertyInfo对象。

Edit:基于编辑,只要您以特定于您正在创建的对象的某种方式实现GetValue方法,您就可以使用该类。不清楚您正在使用DynamicClass实例做什么,或者更重要的是,属性值应该存储在哪里。

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

由于PropertyInfo是一个抽象类,您将不得不自己实现它:

class MyPropertyInfo : PropertyInfo
{
}

有一大堆的方法和属性你需要覆盖,但因为你只会查询NameType,你可以简单地throw new InvalidOperationException()在其他的。

由于数组的大小是固定的,所以不可能将伪PropertyInfo添加到数组中,但是您可以创建一个List<PropertyInfo>,将原始数组和伪PropertyInfo添加到其中,并返回theList.ToArray()

在实现这一点之前,我会考虑其他选项,例如创建自己的MyProperty类,派生自PropertyInfo,而是返回一个数组。

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

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

这是一种非常非常松散的实现方式。但它能让你实现你想要的那些字段;然后将它们填充到数组中。你来处理。

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