2013-03-26 4 views
9

Это мой самый первый пост, и хотя я в определенной степени искал темы, связанные с моей проблемой, у меня возникли проблемы с поиском правильного ответа.Создайте новый объект PropertyInfo на лету

Мой вопрос может быть очень простым, но я знаю, что ответ не так-то просто дать. Если какой-либо существует вообще.

С этим, как говорится, это мой случай: в качестве примера, у меня есть массив объектов PropertyInfo, которые я использую, чтобы получить свойства из класса, как это:

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

    return properties; 
} 

Выглядит легко , правильно? Теперь моя проблема заключается в следующем: как создать новый объект PropertyInfo и добавить его в массив?

Я видел другие сообщения, где пользователи хотят установить VALUE в PropertyInfo, но это не то, что мне нужно. Мне нужно создать на лету новый объект PropertyInfo, где есть только доступные данные: Название и Тип.

Испытуемый случай, который я опубликовал ранее, является лишь небольшим примером того, чего я пытаюсь достичь. Моя истинная и конечная цель, на самом деле, чтобы быть в состоянии создать новый PropertyInfo на основе этого класса:

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

Я надеюсь, что кто-то может помочь мне достичь этого. Спасибо заранее!

EDITED: Я забыл добавить o.GetType() перед методом GetProperties(). Спасибо Илье Иванов!

Я вызова метода 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.

Однако, когда я пытаюсь вызвать базу с параметрами, она дает мне сообщение об ошибке, и я цитирую «Не могу назвать абстрактный элемент:« System.Reflection.PropertyInfo.GetAccessesors (bool) ». Если я попытаюсь вызвать метод без каких-либо параметров, он не обнаруживает никаких ошибок, но я чувствую, что это неправильный подход.

Это то, что я получил до сих пор:

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

return temp; 
} 

UPDATE 2:

Хорошо, что не работает хорошо. После нескольких часов попыток сделать производный класс либо PropertyInfo, либо PropertyDescriptor, я решил не заниматься этим подходом.

Вместо этого у меня была другая идея - читать другие сообщения. Моя истинная проблема заключается в том, что класс, который я обычно читаю и использую для получения свойств, не всегда одинаковый. Поэтому я понял, что мне, вероятно, действительно нужно, это просто способ создать динамический класс на лету, и только , затем.

Я читал, что существует такая вещь, как ExpandoObject и ElasticObject, хотя я еще не знаю, как применить их к моей проблеме, чтобы получить окончательное решение.

Хорошо, теперь, что я действительно делаю это -> Я использовал решение, указанное в следующей ссылке: jQuery DataTables Plugin Meets C#.

Дело в том, Это предполагает, я буду иметь различные статические модели/классы для каждой БД Таблица. Однако в моем случае у меня будет два типа столбцов: те, которые предоставляются каждым классом таблицы DB (также называются базовыми столбцами), а затем дополнительные столбцы, которые я динамически поставляю уже в моей адаптации.

Например: если класс DB таблица:

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

А потом поставить дополнительный столбец под названием «Действие» строкового типа, то в классе DataTableParser, в то _Недвижимости 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 

И честно говоря, что это ВСЕ мне нужно! Ни больше ни меньше! В остальном я уже разбираюсь!

В конце концов, поскольку у меня есть другое количество столбцов (поставлено), чем объект, переданный классу DataTableParser, он всегда дает ошибку во время сортировки и фильтрации DataTable.

Любая помощь пожалуйста? Мне это действительно нужно! Еще раз спасибо.

С наилучшими пожеланиями,

Луис

+0

Обычно вы проходите через 'aType.GetProperties'. –

+1

'typeof (o)' это не будет компилироваться. Вероятно, вы имеете в виду 'o.GetType(). GetProperties()' –

+2

Как вы собираетесь использовать этот объект (ы), который вы пытаетесь создать? – Andrei

ответ

9

Решение:

Хорошо, в основном то, что я сделал было повторно использовать класс MyTypeBuilder (с некоторыми изменениями) от темы ниже, чтобы создать динамический объект и добавил метод, чтобы получить Илист от него.

Ссылка: 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, как так, чтобы иметь информацию о конкретном поле

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

Это решение выходит немного за пределами моего первоначального вопроса, но он показывает, как создать объект динамически, следовательно, позволяет нам добавлять свойства на летать на этот объект.

0

Поскольку PropertyInfo является абстрактным классом, вы должны реализовать ее самостоятельно:

class MyPropertyInfo : PropertyInfo 
{ 
} 

Там целая куча методов и свойств, которые необходимо переопределить но так как вы только запрашиваете Name и Type, вы можете просто throw new InvalidOperationException() на других.

Добавление поддельный PropertyInfo в массив не представляется возможным как массив фиксированного размера, но вы можете создать List<PropertyInfo>, добавить исходный массив и поддельный PropertyInfo к нему, и вернуть theList.ToArray().

Я хотел бы рассмотреть другие варианты, прежде чем осуществление этого, хотя, например, создать свой собственный класс, который MyPropertyне проистекают из PropertyInfo и возвращает массив из тех, вместо этого.

+0

Может быть, 'NotImplementedException' лучше подходит? – Andrei

+0

@Andrei Если вы планируете его реализовать, но еще не закончили, бросьте 'NotImplementedException'. Если вы не планируете его реализовать (например, вам не нужна функция для конкретной реализации), бросьте 'NotSupportedException'. –

+0

@ C.Evenhuis: Но как использовать List без добавления в список объектов PropertyInfo? И как мне создать ** те **? – MadGatsu

4

PropertyInfo - это абстрактный класс. Если вам нужны только объекты Name и PropertyType, вы можете получить свой собственный класс, чтобы предоставить эту информацию.

Места, где вы могли бы использовать эти экземпляры, крайне ограничены. Чтобы помочь вам лучше, нам нужно знать точно, как вы планируете использовать объекты 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(); 
    } 
} 
+0

Mate Я отредактировал основную тему, чтобы показать, как именно я использую массив PropertyInfo с IQueryable. – MadGatsu

+0

@ LuisVale Вам нужно попробовать это и посмотреть, работает ли оно. Я добавил изменение, чтобы указать, что вам придется реализовать метод GetValue каким-либо значимым образом. –

+0

А потом я просто буду использовать его вот так? Список PropertyList = новый список (); Свойство SimplePropertyInfo = new SimplePropertyInfo (_name, _type); PropertyList.Add (SimplePropertyInfo); PropertyInfo [] propArray = PropertyList.ToArray(); – MadGatsu

0

У вас есть 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(); 

Это очень и очень свободный способ реализовать это. Но это позволит вам реализовать те области, которые вам нравятся; затем заполните их в массив. Для вас.

Возможно, вы неправильно поняли свой вопрос, но я мог бы предложить Полиморфизм.

Смежные вопросы