11

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

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

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

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

    return properties;
}

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

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

我之前发布的测试用例只是我想要实现的一个小例子。事实上,我真正的最终目标是能够基于这个类创建一个新的 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();
        }
    }

此致,

路易斯


更新

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

好的,现在我真正要做的是 -> 我一直在使用以下链接中提到的解决方案:jQuery DataTables Plugin Meets C#。

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

例如:如果这是 DB 表类:

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

然后我提供了一个额外的列,名为“Action”,类型为 string,然后在 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 进行排序和过滤时总是会出错。

请问有什么帮助吗?我真的很需要!再次感谢。

此致,

路易斯

4

4 回答 4

10

解决方案:

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

链接:如何在 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;
}

最后,我在某个地方找到了这个方法,它允许人们将值设置为成员:

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

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

于 2013-04-03T09:14:06.847 回答
8

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

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

编辑:根据编辑,只要您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();
    }
}
于 2013-03-26T16:09:33.887 回答
0

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

class MyPropertyInfo : PropertyInfo
{
}

您需要覆盖一大堆方法和属性,但由于您只会查询Nameand Type,因此您可以简单地throw new InvalidOperationException()在其他方法和属性上进行覆盖。

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

不过,在实现此之前,我会考虑其他选项,例如创建您自己的MyProperty类,该类派生自这些类PropertyInfo并返回一个数组。

于 2013-03-26T16:04:21.507 回答
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();

这是一种非常非常松散的实现方式。但它可以让你实现你想要的那些领域;然后将它们填充到数组中。给你处理。

我可能误解了你的问题,但我可能会建议Polymorphism

于 2013-03-26T16:10:31.280 回答