c# - usar - tipos de delegates
Crear un delegado setter de propiedades (4)
La API Expression
admite esto en .NET 4.0, pero, por desgracia, el compilador C # no agrega ningún caramelo extra para apoyar. Pero la buena noticia es que puedes 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í están todos, para obtener información:
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
}
}
He creado métodos para convertir una propiedad lambda a un delegado:
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 estos no compilarán:
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 explícitamente".
¿Es lo que estoy tratando de hacer posible? Gracias por adelantado.
Según mis comentarios, porque los enlaces fallan, he publicado el código completo como 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 solo un enlace .
// 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;
}
Su MakeSetter
está esperando una Action<T>
y la está pasando a Func<T>
( () => someObject.SomeProperty
). Pruebe lo siguiente:
Delegate setter = MakeSetter((prop) => {someObject.SomeProperty = prop;});
setter.DynamicInvoke(new object[]{propValue});
EDITAR Parece que no puedes convertir expresiones lambdas en expresiones . Esta es una especie de ronda sobre cómo 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});
}
}
Action<T>
representa un delegado que toma un parámetro de tipo T
y no devuelve nada. Las expresiones lambda que proporciona a 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 al hecho de 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á sincronizado.