Я создал методы для преобразования лямбды свойства делегату:
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;
}
Они работают:
Delegate getter = MakeGetter(() => SomeClass.SomeProperty);
object o = getter.DynamicInvoke();
Delegate getter = MakeGetter(() => someObject.SomeProperty);
object o = getter.DynamicInvoke();
но они не скомпилируют:
Delegate setter = MakeSetter(() => SomeClass.SomeProperty);
setter.DynamicInvoke(new object[]{propValue});
Delegate setter = MakeSetter(() => someObject.SomeProperty);
setter.DynamicInvoke(new object[]{propValue});
Сбой строк MakeSetter с "Аргументами типа не может быть выведен из использования. Попытайтесь указать аргументы типа явно".
То, что я пытаюсь сделать возможный?Заранее спасибо.
Ваш MakeSetter
ожидает Action
, а вы передаете ему Func
( () => someObject. SomeProperty
). Попробуйте следующее:
Delegate setter = MakeSetter((prop) => {someObject.SomeProperty = prop;});
setter.DynamicInvoke(new object[]{propValue});
EDIT Не похоже, что вы можете преобразовать лямбда-выражения операторов в выражения . Это своего рода обходной способ сделать это без выражений - прямо к делегатам:
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
и ничего не возвращает. Лямбда-выражения, которые вы предоставляете для MakeSetter
, представляют делегатов, которые не принимают параметров и возвращают либо SomeClass.SomeProperty
, либо someObject.SomeProperty
.
Сообщения об ошибках, которые вы получаете, связаны с тем, что компилятор не может вывести типы из лямбда-выражений, которые вы передаете в метод MakeSetter
, потому что то, что вы передали, и какой метод ожидает, не синхронизированы.
API Expression
поддерживает это в .NET 4.0, но, к сожалению, компилятор C# не добавляет никаких дополнительных конфет для поддержки. Но есть и хорошая новость: вы можете тривиально взять выражение "get" (которое компилятор C# может написать) и переписать его как выражение "set".
И даже лучше: если у вас нет .NET 4.0, есть еще как минимум два способа выполнить "set" через выражение, написанное как "get".
Вот они, для информации:
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
}
}