2013-05-08 40 views
8

Ich möchte einen Lambda-Ausdruck für jede Eigenschaft eines Objekts erstellen, der den Wert dynamisch liest.Lesen von Eigenschaften eines Objekts mit Ausdrucksstrukturen

Was ich bisher haben:

var properties = typeof (TType).GetProperties().Where(p => p.CanRead); 

foreach (var propertyInfo in properties) 
{ 
    var getterMethodInfo = propertyInfo.GetGetMethod(); 

    var entity = Expression.Parameter(typeof (TType)); 

    var getterCall = Expression.Call(entity, getterMethodInfo); 

    var lambda = Expression.Lambda(getterCall, entity); 
    var expression = (Expression<Func<TType, "TypeOfProperty">>) lambda; 
    var functionThatGetsValue = expression.Compile(); 
} 

Der Code gut funktioniert, wenn ich functionThatGetsValue solange „TypeOfProperty“ nennen, ist hartcodiert. Ich weiß, dass ich die "TypeOfProperty" nicht dynamisch weitergeben kann. Was kann ich tun, um mein Ziel zu erreichen?

+0

Was ist Ihr Ziel? Sie sagen, dass Sie einen Lambda-Ausdruck erstellen möchten. Benötigen Sie nur den kompilierten Delegaten ('functionThatGetsValue'), oder benötigen Sie auch den intermediate Ausdrucksbaum (' expression')? – LukeH

+0

@LukeH, nur der kompilierte Delegat. Vielen Dank. (Mein Ziel ist es, durch eine Liste von Objekten zu iterieren und alle Werte aus den Eigenschaften zu lesen. Um ein bisschen Leistung zu bekommen, möchte ich es auf diese Weise tun, anstatt Reflektion zu verwenden) – gsharp

+1

Als ich versuchte, ein ähnliches Ergebnis zu erzielen, endete ich mit Rückgabe von Func und Rückgabewert an bestimmten Eigenschaftstyp auf der Seite des Aufrufers zurückgegeben. –

Antwort

7

Unter der Annahme, dass Sie mit einem Func<TType, object> Delegierten zufrieden sind (gemäß den obigen Ausführungen), können Sie Expression.Convert das erreichen verwenden:

var properties = typeof(TType).GetProperties().Where(p => p.CanRead); 

foreach (var propertyInfo in properties) 
{ 
    MethodInfo getterMethodInfo = propertyInfo.GetGetMethod(); 
    ParameterExpression entity = Expression.Parameter(typeof(TType)); 
    MethodCallExpression getterCall = Expression.Call(entity, getterMethodInfo); 

    UnaryExpression castToObject = Expression.Convert(getterCall, typeof(object)); 
    LambdaExpression lambda = Expression.Lambda(castToObject, entity); 

    var functionThatGetsValue = (Func<TType, object>)lambda.Compile(); 
} 
5

Nach Stunden googeln die Antwort here gefunden. Ich habe den Schnipsel aus dem Blog-Eintrag hinzugefügt, wie es andere vielleicht helfen, die gleichen Probleme haben:

public static class PropertyInfoExtensions 
{ 
    public static Func<T, object> GetValueGetter<T>(this PropertyInfo propertyInfo) 
    { 
     if (typeof(T) != propertyInfo.DeclaringType) 
     { 
      throw new ArgumentException(); 
     } 

     var instance = Expression.Parameter(propertyInfo.DeclaringType, "i"); 
     var property = Expression.Property(instance, propertyInfo); 
     var convert = Expression.TypeAs(property, typeof(object)); 
     return (Func<T, object>)Expression.Lambda(convert, instance).Compile(); 
    } 

    public static Action<T, object> GetValueSetter<T>(this PropertyInfo propertyInfo) 
    { 
     if (typeof(T) != propertyInfo.DeclaringType) 
     { 
      throw new ArgumentException(); 
     } 

     var instance = Expression.Parameter(propertyInfo.DeclaringType, "i"); 
     var argument = Expression.Parameter(typeof(object), "a"); 
     var setterCall = Expression.Call(
      instance, 
      propertyInfo.GetSetMethod(), 
      Expression.Convert(argument, propertyInfo.PropertyType)); 
     return (Action<T, object>)Expression.Lambda(setterCall, instance, argument).Compile(); 
    } 
} 
0

I gsharp den Beitrag oben geändert habe, um tatsächlich den Wert direkt eingestellt und macht es ein bisschen einfacher zu bedienen. Es ist nicht ideal, da es die Einführung der DynamicCast-Funktion gibt, bei der Sie Ihren Typ im Voraus kennen müssen. Mein Ziel war es, uns stark typisiert zu halten und kein Objekt zurückzugeben und dynamisches Schlüsselwort zu vermeiden. Halten Sie auch "Magie" auf ein Minimum.

public static T DynamicCast<T>(this object value) 
    { 
     return (T) value; 
    } 
    public static object GetPropertyValue<T>(this PropertyInfo propertyInfo, T objectInstance) 
    { 
     if (typeof(T) != propertyInfo.DeclaringType) 
     { 
      throw new ArgumentException(); 
     } 

     var instance = Expression.Parameter(propertyInfo.DeclaringType, "i"); 
     var property = Expression.Property(instance, propertyInfo); 
     var convert = Expression.TypeAs(property, propertyInfo.PropertyType); 
     var lambda = Expression.Lambda(convert, instance).Compile(); 
     var result = lambda.DynamicInvoke(objectInstance); 


     return result; 
    } 

    public static void SetPropertyValue<T, TP>(this PropertyInfo propertyInfo, T objectInstance, TP value) 
     where T : class 
     where TP : class 
    { 
     if (typeof(T) != propertyInfo.DeclaringType) 
     { 
      throw new ArgumentException(); 
     } 

     var instance = Expression.Parameter(propertyInfo.DeclaringType, "i"); 
     var argument = Expression.Parameter(propertyInfo.PropertyType, "a"); 
     var setterCall = Expression.Call(
      instance, 
      propertyInfo.GetSetMethod(), 
      Expression.Convert(argument, propertyInfo.PropertyType)); 

     var lambda = Expression.Lambda(setterCall, instance, argument).Compile(); 
     lambda.DynamicInvoke(objectInstance, value); 
    } 

Beispiele:

 public void Get_Value_Of_Property() 
    { 
     var testObject = new ReflectedType 
     { 
      AReferenceType_No_Attributes = new object(), 
      Int32WithRange1_10 = 5, 
      String_Requires = "Test String" 
     }; 

     var result = testObject.GetType().GetProperty("String_Requires").GetPropertyValue(testObject).DynamicCast<string>(); 

     result.Should().Be(testObject.String_Requires); 
    } 

    public void Set_Value_Of_Property() 
     { 
      var testObject = new ReflectedType 
      { 
       AReferenceType_No_Attributes = new object(), 
       Int32WithRange1_10 = 5, 
       String_Requires = "Test String" 
      }; 

      testObject.GetType().GetProperty("String_Requires").SetPropertyValue(testObject, "MAGIC"); 

      testObject.String_Requires.Should().Be("MAGIC"); 
     } 

Sie konnte eine Hilfsmethode schreiben, die Makegenericmethod oder einen Ausdruck Baum verwendet eine Lambda auszukommen den typisierten Anruf DynamicCast basierend auf dem Propertyinfo-Objekt zu rufen und zu vermeiden, um es im Voraus zu wissen. Aber das ist weniger elegant.

Verwandte Themen