创建属性setter委托

时间:2010-05-12 22:33:50

标签: c# properties lambda delegates

我创建了将属性lambda转换为委托的方法:

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行失败,“无法根据用法推断出类型参数。请尝试明确指定类型参数。”

我正在尝试做什么?提前谢谢。

4 个答案:

答案 0 :(得分:52)

Expression API在.NET 4.0中支持此功能,但遗憾的是C#编译器不会添加任何额外的支持。但好消息是,您可以轻而易举地采用“获取”表达式(C#编译器可以写入)并将其重写为“set”表达式。

甚至更好;如果你没有.NET 4.0,那么仍然有至少通过写成“get”的表达式执行“set”的其他两种方式。

在这里,他们都是,信息:

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

答案 1 :(得分:16)

根据我的评论 - 因为链接已经死了 - 我已经发布了完整的代码作为问题的答案。 是可以执行OP请求的内容。这是尼克展示它的一个不错的小宝石。 Nick将此页面和其他页面归功于他的完整解决方案以及性能指标。我在下面提供的不是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;
}

答案 2 :(得分:4)

Action<T>表示一个委托,它接受一个T类型的参数并且不返回任何内容。您提供给MakeSetter的lambda表达式表示不带参数并返回SomeClass.SomePropertysomeObject.SomeProperty的委托。

您获得的错误消息是由于编译器无法推断您传递给MakeSetter方法的lambda表达式中的类型,因为您已经传递了什么以及该方法的期望是什么不同步。

答案 3 :(得分:4)

您的MakeSetter期待Action<T>,并且您传递Func<T>() => someObject.SomeProperty)。请尝试以下方法:

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

编辑看起来不像convert statement lambdas into expressions。这有点像没有表达式的方式 - 直接代表:

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