2010-05-12 124 views
31

我創建方式,一種是屬性拉姆達轉換成代表:創建一個屬性setter委託

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行會失敗,並且「無法根據用法推斷類型參數,請嘗試指定類型參數e xplicitly「。

是我想要做的可能嗎?提前致謝。

回答

4

Action<T>代表一個委託,其中一個參數的類型爲T並且不返回任何內容。您提供給MakeSetter的lambda表達式代表不帶參數並返回SomeClass.SomePropertysomeObject.SomeProperty的代表。

您得到的錯誤消息是由於編譯器無法從您傳遞到MakeSetter方法的lambda表達式中推斷出類型,因爲您傳遞的方法以及該方法所期待的內容不在同步。

+0

什麼lambda會使這項工作? – 2010-05-12 23:01:19

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

我得到「帶有語句正文的lambda表達式不能轉換爲表達式樹」。 – 2010-05-12 22:54:04

+0

可能需要手動創建表達式。給我一秒鐘。 – 2010-05-12 23:11:27

47

Expression API支持這在.NET 4.0中,但遺憾的是C#編譯器不會增加任何額外的糖果支持。但好消息是,你可以簡單地採用「get」表達式(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 
    } 
} 
+2

也許一個愚蠢的問題,但爲什麼'/'而不是'*'爲斜體? – 2010-11-03 05:30:18

+1

@Camilo BBS舊習難改 – 2011-06-22 14:43:42

+0

這對我非常有幫助!在MethodInfo上使用Invoke時的一個細微差別是該類型不必完全匹配。因此,您可以創建一個生成的MakeSetter 方法,併爲其指定一個Expression >。如果你嘗試的話,另外兩種方法在運行時失敗。 – 2012-06-29 18:19:28

8

按我的意見 - 因爲鏈接會進入死 - 我已經發布了完整的代碼作爲一個問題的答案。 是的,有可能做OP的要求。這是Nick展示它的一個不錯的小寶石。 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; 
} 
+0

這很好。 SO上唯一的非愚蠢的委託解決方案。你是我的英雄;-)我知道純粹用表情來做它一定是可以的,但我監督着「指定」。 – t3chb0t 2016-12-31 06:06:26