2013-03-26 16 views
9

To jest mój pierwszy post i chociaż w pewnym stopniu szukałem tematów związanych z moim problemem, mam wiele problemów ze znalezieniem właściwej odpowiedzi.Utwórz nowy obiekt PropertyInfo w locie

Moje pytanie może być bardzo proste, ale zdaję sobie sprawę, że odpowiedź może nie być tak łatwa do podania. Jeśli w ogóle istnieje.

Z tym mówi, to jest moja sprawa: jako przykład, mam tablicę obiektów PropertyInfo, które używam, aby uzyskać właściwości z klasy, jak to:

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

    return properties; 
} 

Wygląda łatwe , dobrze? Teraz mój problem jest następujący: jak utworzyć obiekt PropertyInfo i dodać go do tablicy?

Widziałem inne posty, w których użytkownicy chcą ustawić wartość VALUE PropertyInfo, ale to nie jest to, czego potrzebuję. Potrzebuję stworzyć nowy obiekt PropertyInfo, w którym jedynymi dostępnymi danymi są: i Type.

Opublikowany wcześniej test to tylko mały przykład tego, co próbuję osiągnąć. Mój prawdziwy i ostateczny cel jest w rzeczywistości, aby móc utworzyć nowy PropertyInfo na podstawie tej klasy:

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

Mam nadzieję, że ktoś może mi pomóc w osiągnięciu tego celu. Z góry bardzo dziękuję!

EDYCJA: Zapomniałem dodać o.GetType() przed metodą GetProperties(). Dzięki, Ilya Ivanov!

Wołam do SelectProperties metody tak:

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

metoda wygląda następująco:

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

pozdrowieniami,

Luis


UPDATE:

OK, więc śledzę rady 280Z28 i dziedziczę PropertyInfo w nowej klasie. Zrobiłem więcej badań i znalazłem w MSDN, że muszę zastąpić następujące metody: GetValue, SetValue, GetAccessors, GetGetMethod, GetSetMethod i GetIndexParameters.

Jednak gdy próbuję wywołać bazę z parametrami, daje mi to błąd i cytuję "Nie można wywołać abstrakcyjnego elementu:" System.Reflection.PropertyInfo.GetAccessesors (bool) "". Jeśli spróbuję wywołać metodę bez żadnych parametrów, nie pojawi się żaden błąd, ale mam wrażenie, że to niewłaściwe podejście.

To, co mam do tej pory:

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

return temp; 
} 

UPDATE 2:

OK, to nie działa dobrze. Po kilku godzinach prób uzyskania klasy pochodnej albo PropertyInfo albo PropertyDescriptor, zdecydowałem się nie stosować tego podejścia.

Zamiast tego miałem inny pomysł na czytanie innych postów. Mój prawdziwy problem polega na tym, że klasa, którą zwykle czytam i używam, aby uzyskać właściwości, nie zawsze jest taka sama. Tak więc zdałem sobie sprawę, czego naprawdę potrzebuję, to po prostu sposób na stworzenie w locie dynamicznej klasy , a tylko , a następnie uzyskać właściwości.

Przeczytałem, że jest coś takiego o nazwie ExpandoObject i ElasticObject, chociaż jeszcze nie wiem, jak zastosować je do mojego problemu, aby ostatecznie uzyskać rozwiązanie.

Ok teraz, co ja naprawdę robię to jest -> I zostały z wykorzystaniem roztworu wzmiankowanym w poniższym linku: jQuery DataTables Plugin Meets C#.

Chodzi o to, to zakłada się, że mają różne modele statyczne/zajęcia dla każdego DB stół. Jednak w moim przypadku będę miał dwa rodzaje kolumn: te dostarczone przez każdą klasę tabeli DB (również podstawowe kolumny), a następnie dodatkowe kolumny, które dynamicznie dostarczam już w mojej adaptacji.

Na przykład: jeśli ta klasa tabela DB:

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

I wtedy dostarczyć dodatkową kolumnę o nazwie „Akcja” typu ciąg, następnie w klasie DataTableParser w _properties attribure nie powinien być następujące informacje:

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

I szczerze mówiąc, że jest ALL muszę! Nic dodać nic ująć! Resztę już parsuję!

W końcu, ponieważ mam inną liczbę kolumn (w zestawie) niż obiekt przekazany do klasy DataTableParser, zawsze daje błąd podczas sortowania i filtrowania DataTable.

Proszę o pomoc? Naprawdę tego potrzebuję! Dzięki jeszcze raz.

poważaniem,

Luis

+0

zwykle przechodzi się przez 'aType.GetProperties'. –

+1

'typeof (o)' to się nie skompiluje. Prawdopodobnie masz na myśli 'o.GetType(). GetProperties()' –

+2

Jak zamierzasz używać tego obiektu (obiektów), który próbujesz utworzyć? – Andrei

Odpowiedz

9

Rozwiązanie:

Ok, w zasadzie to, co zrobiłem było ponowne klasę MyTypeBuilder (z pewnymi zmianami) z tematu poniżej w celu stworzenia dynamicznego obiektu i dodaje metodę, aby uzyskać ILIST z tego.

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

Również użyłem klasy polowego jak tak, aby uzyskać informacje na temat danej dziedzinie

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

Wreszcie znalazłem tej metody gdzieś, że pozwala osobie, aby ustawić wartość dla użytkownika:

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

Potem zrobiłem śledzenie g do tworzenia dynamicznych obiektu i wstawić obiekt na nim:

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

rozwiązanie to idzie trochę ponad moje początkowe pytanie, ale nie pokazuje, jak stworzyć obiekt dynamicznie, stąd pozwala nam dodać właściwości na lecę do tego obiektu.

0

Od PropertyInfo jest klasą abstrakcyjną, trzeba będzie go realizować siebie:

class MyPropertyInfo : PropertyInfo 
{ 
} 

Jest cała masa metod i właściwości trzeba zastąpić ale ponieważ będziesz tylko wyszukiwać Name i Type, możesz po prostu throw new InvalidOperationException() na innych.

Dodanie fałszywy PropertyInfo do tablicy nie jest możliwe, ponieważ tablica jest stałym rozmiarze, ale można utworzyć List<PropertyInfo>, dodać oryginalną tablicę i fałszywy PropertyInfo do niego i wrócić theList.ToArray().

chciałbym rozważyć inne opcje przed realizacji tego choć na przykład utworzyć własną klasę MyProperty że nie pochodzą z PropertyInfo i zwraca tablicę tych zamiast.

+0

Może "NotImplementedException" jest tu lepiej dopasowany? – Andrei

+0

@Andrei Jeśli planujesz go wdrożyć, ale jeszcze nie skończyłeś, wyrzuć 'NotImplementedException'. Jeśli nie planujesz go wdrożyć (np. Nie potrzebujesz funkcji dla konkretnej implementacji), wyślij 'NotSupportedException'. –

+0

@ C.Evenhuis: Ale jak używać listy bez dodawania do listy obiektów PropertyInfo? A jak mam stworzyć ** te **? – MadGatsu

4

PropertyInfo to klasa abstrakcyjna. Jeśli potrzebujesz tylko właściwości Name i PropertyType, możesz wyprowadzić z nich swoją własną klasę, aby podać te informacje.

Miejsca, w których można wykorzystać te wystąpienia, są bardzo ograniczone. Aby pomóc Ci lepiej, będziemy potrzebować dokładnie poznać sposób, w jaki zamierzasz korzystać z obiektów, które próbujesz utworzyć.

Edit: Na podstawie edycji, może być w stanie używać tej klasy tak długo, jak można zaimplementować metodę w pewien sposób specyficzny dla obiektów tworzysz GetValue. Nie jest jasne, co robisz z instancjami DynamicClass lub, co ważniejsze, gdzie wartości właściwości mają być przechowywane.

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 Edytowałem główny temat, aby pokazać, jak dokładnie używam tablicy PropertyInfo z IQueryable. – MadGatsu

+0

@LuisVale Musisz spróbować i sprawdzić, czy to działa. Dodałem edycję, aby wskazać, że będziesz musiał wdrożyć metodę 'GetValue' w jakiś znaczący sposób. –

+0

A potem po prostu zrobiłbym to tak? Lista Lista właściwości = nowa lista (); Właściwość SimplePropertyInfo = new SimplePropertyInfo (_name, _type); PropertyList.Add (SimplePropertyInfo); PropertyInfo [] propArray = PropertyList.ToArray(); – MadGatsu

0

Masz Method który ma wspólność z kilku innych przypadkach, najbardziej efektywny sposób obsługiwać takie zdarzenie jest przez polimorfizmu.

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

Następnie można wykonać następujące czynności:

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

i tak dalej ...

Teraz trzeba oczywiście dodawać implementację w jaki sposób wypełnić te pozycje w górę; ale możesz po prostu zadzwonić:

DynamicClass[] obj = new DynamicClass[] 

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

Jest to bardzo, bardzo luźny sposób realizacji tego. Ale pozwoliłoby ci wprowadzić te pola, tak jak chcesz; następnie wypełnij je do tablicy. Do załatwienia.

Mogłem źle zrozumieć twoje pytanie, ale mogę zasugerować Polimorfizm.