2013-03-26 11 views
9

Dies ist mein allererster Beitrag, und obwohl ich in Themen zu meinem Problem in gewissem Umfang gesucht habe, habe ich eine Menge Probleme bei der Suche nach der richtigen Antwort.Erstellen Sie ein neues PropertyInfo-Objekt im laufenden Betrieb

Meine Frage kann sehr einfach sein, aber ich bin mir bewusst, dass die Antwort nicht so einfach zu geben ist. Wenn überhaupt etwas existiert.

Mit diesem wird gesagt, das ist mein Fall: als Beispiel, ich habe eine Reihe von Propertyinfo-Objekte, die ich die Eigenschaften von einer Klasse zu erhalten verwende, wie folgt aus:

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

    return properties; 
} 

Sieht einfach aus , Recht? Jetzt ist mein Problem das: wie ein neues PropertyInfo-Objekt erstellen und es dem Array hinzufügen?

Ich habe andere Beiträge gesehen, wo Benutzer den Wert einer PropertyInfo festlegen möchten, aber das ist nicht was ich brauche. Was ich brauche, ist im Handumdrehen ein neues PropertyInfo-Objekt zu erstellen, wobei die einzigen verfügbaren Daten, die ich habe, die Name und die Type sind.

Der Testfall, den ich zuvor veröffentlicht habe, ist nur ein kleines Beispiel dessen, was ich erreichen möchte. Meine wahre und endgültige Ziel ist, in der Tat, auf dieser Klasse eine neue Property Basis zu schaffen, in der Lage sein:

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

Ich hoffe jemand kann mir helfen, dies zu erreichen. Vielen Dank im Voraus!

EDITED: Ich habe vergessen, o.GetType() vor GetProperties() -Methode hinzuzufügen. Danke Ilja Iwanow!

Ich rufe die Methode SelectProperties wie so:

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

Das Verfahren sieht wie folgt aus:

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

Mit freundlichen Grüßen,

Luis


AKTUALISIEREN:

Ok, also befolge ich den 280Z28-Rat und erwerbe PropertyInfo in einer neuen Klasse. Ich habe mehr Untersuchungen durchgeführt und in MSDN festgestellt, dass ich die folgenden Methoden überschreiben muss: GetValue, SetValue, GetAccessors, GetGetMethod, GetSetMethod und GetIndexParameters.

Wenn ich jedoch versuche, Base mit den Parametern aufzurufen, gibt es mir einen Fehler und ich zitiere "Kann ein abstraktes Mitglied nicht aufrufen: 'System.Reflection.PropertyInfo.GetAccessors (bool)'". Wenn ich versuche, die Methode ohne Parameter aufzurufen, wird kein Fehler angezeigt, aber ich denke, das ist der falsche Ansatz.

Dies ist, was ich bisher habe:

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

return temp; 
} 

UPDATE 2:

Ok, das ist nicht gut funktionierte. Nach einigen Stunden, in denen ich versucht habe, die abgeleitete Klasse von PropertyInfo oder PropertyDescriptor auszuführen, habe ich mich entschieden, diesen Ansatz nicht durchzuführen.

Stattdessen hatte ich eine andere Idee aus dem Lesen anderer Beiträge. Mein wahres Problem liegt in der Tatsache, dass die Klasse, die ich normalerweise lese und verwende, um die Eigenschaften zu erhalten, nicht immer die gleiche ist. So realisierte ich, was ich wahrscheinlich wirklich brauche, ist nur eine Möglichkeit, eine dynamische Klasse on the fly zu erstellen, und nur dann erhalten Sie die Eigenschaften.

Ich habe gelesen, dass es so etwas wie ExpandoObject und ElasticObject gibt, obwohl ich noch nicht ganz weiß, wie man sie auf mein Problem anwendet, um endlich eine Lösung zu bekommen.

Ok jetzt, was ich wirklich tun, ist dies -> Ich habe in den folgenden Link unter Verwendung der Lösung genannt: jQuery DataTables Plugin Meets C#.

Die Sache ist, Dies setzt voraus, ich verschiedene statische Modelle/Klassen für jede DB haben Tabelle. In meinem Fall werde ich jedoch zwei Arten von Spalten haben: Die, die von jeder DB-Tabellenklasse (aka Basisspalten) zur Verfügung gestellt werden, und dann zusätzliche Spalten, die ich bereits dynamisch in meiner Anpassung bereitstelle.

Zum Beispiel: dies, wenn die DB-Tabellenklasse:

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

Und dann liefere ich eine zusätzliche Spalte „Aktion“ vom Typ String, dann in der DataTableParser Klasse aufgerufen, in dem _properties attribure es sollte die folgenden Angaben:

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

Und um ehrlich zu sein, dass ALL ist ich brauche! Nicht mehr, nicht weniger! Den Rest analysiere ich schon!

Am Ende, da ich eine andere Anzahl von Spalten (geliefert) als das Objekt an die DataTableParser-Klasse übergeben haben, gibt es immer Fehler beim Sortieren und Filtern der DataTable.

Irgendwelche Hilfe bitte? Ich brauche es wirklich! Danke noch einmal.

Mit freundlichen Grüßen,

Luis

+0

normalerweise gehen Sie durch 'aType.GetProperties'. –

+1

'typeof (o)' dies wird nicht kompiliert. Wahrscheinlich meinst du 'o.GetType(). GetProperties()' –

+2

Wie willst du dieses Objekt, das du erstellen willst, benutzen? – Andrei

Antwort

9

Lösung:

Ok, im Grunde, was ich getan habe, war die MyTypeBuilder Klasse Wiederverwendung (mit einigen Anpassungen) aus dem Thema unten, um ein dynamisches Objekt und hinzugefügt, ein Verfahren zu schaffen, ein erhalten IList davon.

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

Auch habe ich eine Klasse Field wie so Informationen zu einem bestimmten Feld

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

Schließlich zu haben, fand ich irgendwo diese Methode, die eine Person ermöglicht einstellen der Wert für ein Mitglied:

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

Danach habe ich das folgende getan g ein dynamisches Objekt zu erstellen und eine Eigenschaft auf sie ein:

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

Diese Lösung geht ein wenig über meine erste Frage, aber es zeigt, wie ein Objekt dynamisch zu erstellen, damit wir ermöglichen Eigenschaften auf dem hinzufügen Fliege zu diesem Objekt.

0

Seit PropertyInfo eine abstrakte Klasse ist, werden Sie es selbst implementieren:

class MyPropertyInfo : PropertyInfo 
{ 
} 

Es gibt eine ganze Reihe von Methoden und Eigenschaften, die Sie außer Kraft setzen müssen aber da Sie nur Name und Type abfragen, könnten Sie einfach throw new InvalidOperationException() auf den anderen.

die gefälschten PropertyInfo zum Array hinzuzufügen ist nicht möglich, da ein Array feste Größe ist, aber man konnte ein List<PropertyInfo>, fügen Sie den Original-Array und die gefälschten PropertyInfo, um es, und das Rück theList.ToArray() zu erstellen.

würde ich andere Optionen prüfen, bevor obwohl diese Umsetzung, zum Beispiel Ihre eigene MyProperty Klasse erstellen, dass stammt nicht aus PropertyInfo und zurück statt eine Reihe von denen.

+0

Vielleicht passt 'NotImplementedException' hier besser? – Andrei

+0

@Andrei Wenn Sie es implementieren möchten, aber noch nicht fertig sind, werfen Sie 'NotImplementedException'. Wenn Sie nicht beabsichtigen, es zu implementieren (z. B. wenn Sie die Funktion für eine bestimmte Implementierung nicht benötigen), müssen Sie "NotSupportedException" auslösen. –

+0

@ C.Evenhuis: Aber wie verwende ich die List , ohne zu der Liste PropertyInfo Objekte hinzuzufügen? Und wie erstelle ich ** diese **? – MadGatsu

4

PropertyInfo ist eine abstrakte Klasse. Wenn Sie nur die Eigenschaften Name und PropertyType benötigen, können Sie daraus eine eigene Klasse ableiten, um diese Informationen bereitzustellen.

Die Orte, an denen Sie diese Instanzen verwenden können, sind extrem eingeschränkt. Um Ihnen besser zu helfen, müssen wir wissen, genau, wie Sie die PropertyInfo Objekte verwenden möchten, die Sie erstellen möchten.

Edit: Basierend auf dem bearbeiten, können Sie in der Lage sein, diese Klasse zu verwenden, solange Sie die GetValue Verfahren in irgendeiner Weise spezifisch für die Objekte implementieren Sie erstellen. Es ist nicht klar, was Sie mit den Instanzen DynamicClass tun, oder noch wichtiger, wo die Eigenschaftswerte gespeichert werden sollen.

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 Ich habe das Hauptthema bearbeitet, um zu zeigen, wie genau ich das PropertyInfo-Array mit dem IQueryable verwende. – MadGatsu

+0

@LuisVale Sie müssen dies versuchen und sehen, ob es funktioniert. Ich fügte eine Änderung hinzu, um anzuzeigen, dass Sie die 'GetValue'-Methode in einer sinnvollen Weise implementieren müssen. –

+0

Und dann würde ich es einfach so benutzen? Liste PropertyList = new Liste (); SimplePropertyInfo Eigenschaft = new SimplePropertyInfo (_name, _type); PropertyList.Add (SimplePropertyInfo); Property [] = propArray PropertyList.ToArray(); – MadGatsu

0

Sie haben eine Method die Gemeinsamkeit mit mehreren anderen Fällen hat die effizienteste Art und Weise ein solches Ereignis ist durch Polymorphismus zu behandeln.

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

Dann können Sie wie folgt vorgehen:

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

Und so weiter ...

Jetzt werden Sie müssen natürlich eine Implementierung auf hinzufügen, wie diese Elemente zu füllen; aber dann könntest du einfach anrufen:

DynamicClass[] obj = new DynamicClass[] 

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

Das ist ein sehr, sehr loser Weg, dies zu implementieren. Aber es würde Ihnen erlauben, jene Felder zu implementieren, wie Sie es wünschen; Fülle sie dann in das Array. Für Sie zu handhaben.

Ich habe vielleicht Ihre Frage missverstanden, aber ich könnte vorschlagen Polymorphismus.

Verwandte Themen