2010-05-12 21 views
31

He creado métodos para convertir una lambda propiedad a un delegado:Creación de un delegado colocador propiedad

public static Delegate MakeGetter<T>(Expression<Func<T>> propertyLambda) 
{ 
    var result = Expression.Lambda(propertyLambda.Body).Compile(); 
    return result; 
} 

public static Delegate MakeSetter<T>(Expression<Action<T>> propertyLambda) 
{ 
    var result = Expression.Lambda(propertyLambda.Body).Compile(); 
    return result; 
} 

Estos trabajos:

Delegate getter = MakeGetter(() => SomeClass.SomeProperty); 
object o = getter.DynamicInvoke(); 

Delegate getter = MakeGetter(() => someObject.SomeProperty); 
object o = getter.DynamicInvoke(); 

pero éstos no se compilará:

Delegate setter = MakeSetter(() => SomeClass.SomeProperty); 
setter.DynamicInvoke(new object[]{propValue}); 

Delegate setter = MakeSetter(() => someObject.SomeProperty); 
setter.DynamicInvoke(new object[]{propValue}); 

Las líneas de MakeSetter fallan con "Los argumentos de tipo no se pueden inferir del uso. Intente especificar los argumentos de tipo e xplicitly ".

¿Es lo que estoy tratando de hacer posible? Gracias por adelantado.

Respuesta

4

Action<T> representa un delegado que toma un parámetro del tipo T y no devuelve nada. Las expresiones lambda que proporciona al MakeSetter representan delegados que no toman ningún parámetro y devuelven SomeClass.SomeProperty o someObject.SomeProperty.

Los mensajes de error que recibe se deben a que el compilador no puede inferir los tipos de las expresiones lambda que está pasando al método MakeSetter porque lo que ha pasado y lo que el método espera no está en sincronizar

+0

¿Qué lambda haría que funcione? –

4

Su MakeSetter está esperando un Action<T> y usted lo está pasando Func<T> (() => someObject.SomeProperty). Pruebe lo siguiente:

Delegate setter = MakeSetter((prop) => {someObject.SomeProperty = prop;}); 
setter.DynamicInvoke(new object[]{propValue}); 

EDITAR no parece que sea posible convert statement lambdas into expressions. Esto es algo de una manera indirecta de hacerlo sin expresiones - directamente a los delegados:

class Test2 { 
    delegate void Setter<T>(T value); 

    public static void Test() { 
     var someObject = new SomeObject(); 
     Setter<string> setter = (v) => { t.SomeProperty = v; }; 
     setter.DynamicInvoke(new object[]{propValue}); 
    } 
} 
+0

Obtengo "Una expresión lambda con un cuerpo de declaración no se puede convertir a un árbol de expresiones". –

+0

podría necesitar crear la expresión manualmente. Dame un segundo. –

47

La API Expression este soporte en .NET 4.0, pero por desgracia el compilador de C# no añade ningún dulce adicional para apoyar. Pero la buena noticia es que se puede tomar trivialmente una expresión "get" (que el compilador de C# puede escribir) y volver a escribirla como una expresión "set".

Y aún mejor; si no tiene .NET 4.0, todavía hay al menos otras dos maneras de realizar un "conjunto" a través de una expresión escrita como "obtener".

Aquí todos ellos son, por Info:

using System; 
using System.Linq.Expressions; 
using System.Reflection; 
class Foo { 
    public string Bar { get; set; } 
    static void Main() { 
     // take a "get" from C# 
     Expression<Func<Foo, string>> get = foo => foo.Bar; 

     // re-write in .NET 4.0 as a "set" 
     var member = (MemberExpression)get.Body; 
     var param = Expression.Parameter(typeof(string), "value"); 
     var set = Expression.Lambda<Action<Foo, string>>(
      Expression.Assign(member, param), get.Parameters[0], param); 

     // compile it 
     var action = set.Compile(); 
     var inst = new Foo(); 
     action(inst, "abc"); 
     Console.WriteLine(inst.Bar); // show it working 

     //==== reflection 
     MethodInfo setMethod = ((PropertyInfo)member.Member).GetSetMethod(); 
     setMethod.Invoke(inst, new object[] { "def" }); 
     Console.WriteLine(inst.Bar); // show it working 

     //==== Delegate.CreateDelegate 
     action = (Action<Foo, string>) 
      Delegate.CreateDelegate(typeof(Action<Foo, string>), setMethod); 
     action(inst, "ghi"); 
     Console.WriteLine(inst.Bar); // show it working 
    } 
} 
+2

Quizás una pregunta tonta, pero ¿por qué el '/' en lugar de '*' para las cursivas? –

+1

@Camilo Viejos hábitos de BBS mueren duro –

+0

¡Esto fue muy útil para mí! Una sutil diferencia al usar Invoke en MethodInfo es que el tipo no tiene que coincidir exactamente. De modo que podría crear un método de generación de MakeSetter y darle una Expresión >. Las otras dos formas fallan en tiempo de ejecución si lo intentas. –

8

Según mis comentarios - porque los enlaces se agotan - He publicado el código completo como una respuesta a la pregunta. SÍ es posible hacer lo que el OP está solicitando. y aquí hay una pequeña joya de Nick que lo demuestra. Nick le atribuye a esta página y a otra página su solución completa junto con las métricas de rendimiento. Lo proporciono a continuación en lugar de just a link.

// returns property getter 
public static Func<TObject, TProperty> GetPropGetter<TObject, TProperty>(string propertyName) 
{ 
    ParameterExpression paramExpression = Expression.Parameter(typeof(TObject), "value"); 

    Expression propertyGetterExpression = Expression.Property(paramExpression, propertyName); 

    Func<TObject, TProperty> result = 
     Expression.Lambda<Func<TObject, TProperty>>(propertyGetterExpression, paramExpression).Compile(); 

    return result; 
} 

// returns property setter: 
public static Action<TObject, TProperty> GetPropSetter<TObject, TProperty>(string propertyName) 
{    
    ParameterExpression paramExpression = Expression.Parameter(typeof(TObject)); 

    ParameterExpression paramExpression2 = Expression.Parameter(typeof(TProperty), propertyName); 

    MemberExpression propertyGetterExpression = Expression.Property(paramExpression, propertyName); 

    Action<TObject, TProperty> result = Expression.Lambda<Action<TObject, TProperty>> 
    (
     Expression.Assign(propertyGetterExpression, paramExpression2), paramExpression, paramExpression2 
    ).Compile(); 

    return result; 
} 
+0

Esto es bueno. La única solución de delegado no estúpido en SO. Eres mi héroe ;-) Sabía que debía ser posible hacerlo puramente con expresiones, pero supervisé el 'Assign'. – t3chb0t

Cuestiones relacionadas