简体   繁体   English

创建一个属性设置器委托

[英]Creating a property setter delegate

I have created methods for converting a property lambda to a delegate: 我创建了将属性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;
}

These work: 这些工作:

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

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

but these won't compile: 但是这些不会编译:

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

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

The MakeSetter lines fail with "The type arguments cannot be inferred from the usage. Try specifying the type arguments explicitly." MakeSetter行失败,并显示“无法从用法中推断出类型实参。尝试显式指定类型实参。”

Is what I'm trying to do possible? 我想做的事可能吗? Thanks in advance. 提前致谢。

The Expression API supports this in .NET 4.0, but sadly the C# compiler doesn't add any extra candy to support. Expression API在.NET 4.0中支持此功能,但遗憾的是C#编译器没有添加任何额外的支持。 But the good news is that you can trivially take a "get" expression (which the C# compiler can write) and re-write it as a "set" expression. 但是好消息是,您可以轻松采用“ get”表达式(C#编译器可以编写)并将其重新编写为“ set”表达式。

And even better; 甚至更好; if you don't have .NET 4.0, there are still at least two other ways of performing a "set" via an expression written as a "get". 如果您没有.NET 4.0,那么至少还有至少两种其他方式可以通过写为“ get”的表达式来执行“ set”。

Here they all are, for 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
    }
}

As per my comments - because links go dead - I have posted the full code as an answer to the question. 根据我的评论-由于链接失效-我已发布完整代码作为问题的答案。 YES it is possible to do what the OP is requesting . 是的,可以执行OP要求的操作 and here is a nice little gem from Nick demonstrating it. 这是尼克(Nick)展示的一个漂亮的小宝石。 Nick credits this page and another page for his complete solution along with performance metrics. Nick将此页面和另一页归功于他的完整解决方案以及性能指标。 I provide that below instead of 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;
}

Action<T> represents a delegate that takes one parameter of type T and returns nothing. Action<T>表示一个委托,该委托采用类型T一个参数,但不返回任何内容。 The lambda expressions you provide to MakeSetter represent delegates that take no parameter and return either SomeClass.SomeProperty or someObject.SomeProperty . 提供给MakeSetter的lambda表达式表示不带参数的委托,并返回SomeClass.SomePropertysomeObject.SomeProperty

The error messages you're getting are due to the fact that the compiler cannot infer the types from the lambda expressions you're passing into the MakeSetter method because what you've passed and what the method is expecting are not in sync. 您收到的错误消息是由于以下事实:编译器无法从传递给MakeSetter方法的lambda表达式中推断类型,因为您传递的内容与该方法期望的内容不同步。

Your MakeSetter is expecting an Action<T> and you are passing it a Func<T> ( () => someObject.SomeProperty ). 您的MakeSetter期望有一个Action<T>并且您要传递给它一个Func<T>() => someObject.SomeProperty )。 Try the following: 请尝试以下操作:

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

EDIT Doesn't look like you can convert statement lambdas into expressions . 编辑看起来您无法将lambdas语句转换为表达式 This is somewhat of a round about way to do it without expressions - straight to delegates: 这是不使用表达式的某种方式的回合-直接针对委托人:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM