2014-01-27 174 views
1

我對你們所有人都有一個有趣的問題。用泛型指定lambda表達式,但使用模板類型

public Func<T, int> GetLambdaFor<T>() 
{ 
    // .... 
} 

public void SetLambdaForAnyType(Func<?, int> lambda) 
{ 
    // .... 
} 

在代碼樣品上方我想看看它是在所有可能的使用SET方法而檢索使用GET方法具體類型Genericlambda expression指定在C#可取代Genericlambda expression

因此,讓我舉一個例子(ofcourse它展示了破C#,我想看看它是可能的,如果是這樣,如何做到這一點):

MyClass.SetLambdaForAnyType<?>((a) => new SomeValueGenerator<?>(a).GetValue()); 
int generateValue = MyClass.GetLambdaFor<string>()("blah"); 
generateValue = MyClass.GetLambdaFor<DateTime>()(DateTime.Now); 
// etc... 

我希望它更有意義現在。有任何想法嗎?如果不可能,還有其他的可能性嗎?

+0

你怎麼能得到一個字符串,並返回一個'int'?或得到一個日期時間並返回一個int?什麼是標準?你真的知道'Func'委託人在做什麼嗎?你從'GetLambdaFor'函數中返回'Func'委託,但你試圖將它分配給一個** int **變量 –

+0

@ Selman22他知道基於提供給'GetLambdaFor'的泛型參數的委託參數。在將結果分配給一個變量之前,他用一個值來調用它,所以這部分也都是Kosher。 – Servy

+0

@ Selman22:他的'GetLambdaFor'總是返回一個返回'int'的函數。 –

回答

3

lambda表達式不能引入泛型參數,有沒有爲它的語法。你將不得不使用泛型方法來代替lambda表達式,並使用反射來構建特定類型的封閉泛型方法,像這樣(代碼未測試):

public Func<T, int> GetLambdaFor<T>() 
{ 
    return GetLambdaFor<T> (m_method, m_target) ; 
} 

public void SetLambdaForAnyType (Func<object, int> lambda) 
{ 
    if (lambda == null) throw new ArgumentNullException ("lambda") ; 

    // delegate complicated signature checks to CLR 
    try 
    { 
     var definition = lambda.Method.GetGenericMethodDefinition() ; 
     GetLambdaFor<Dummy> (definition, lambda.Target) ; 
     m_method = definition ; 
     m_target = lambda.Target ; 
    } 
    catch (Exception e) 
    { 
     throw new ArgumentException ("lambda", e) ; 
    } 
} 

private sealed class Dummy {} 
private MethodInfo m_method ; 
private object  m_target ; 
static Func<T, int> GetLambdaFor<T> (MethodInfo method, object target) 
{ 
    return (Func<T, int>) Delegate.CreateDelegate (typeof (Func<T, int>), 
     target, method.MakeGenericMethod (typeof (T))) ; 
} 

然後你使用這樣

// in your class 
private static int GetValueFromGenerator<T> (T a) 
{ 
    return new SomeValueGenerator<T> (a).GetValue() ; 
} 

MyClass.SetLambdaForAnyType (GetValueFromGenerator) ; 

最後,如果您的'lambda'將始終是這種形式,您可能需要引入一個用於GetValue的接口,而不是創建通用方法,爲這些生成器創建通用類型,激活它們並投射到IGetValue

+0

我有強烈的懷疑,我需要更鬆散耦合的事情,只是我不知道如何處理與泛型lambda表達式。但你的答案似乎足以讓我興奮地嘗試它。好東西! –

+0

我複製了你的代碼,並對它做了一些修復,但它的工作原理!我絕對可以用這個,非常感謝!將在我的實際代碼庫中使用它 - http://renewalprojects.codeplex.com/wikipage?title=EasyProxy%20library&referringTitle=Documentation –

+0

@Dandré您是否在此編輯帖子以包含您的代碼修復? – srm

3

你可以這樣做,當然。你失去了安全型這種類型中,但我們知道它會正確反正從外部來看的工作:

public class Foo 
{ 
    private Dictionary<Type, Delegate> dictionary = 
     new Dictionary<Type, Delegate>(); 

    public Func<T, int> GetLambdaFor<T>() 
    { 
     return dictionary[typeof(T)] as Func<T, int>; 
    } 
    public void SetLambdaFor<T>(Func<T, int> func) 
    { 
     dictionary[typeof(T)] = func; 
    } 
} 

當然,你可能需要使用TryGetValue從字典中,如果你要處理的如果沒有該類型的條目。

另一種選擇是隻使用一個線程靜態字段:

public class Foo2<T> 
{ 
    [ThreadStatic] 
    public Func<T, int> Generator { get; set; } 
} 

然後,它的簡單:

Foo2<string>.Generator = s => 5; 
int result = Foo2<string>.Generator("asdf"); 
+0

它不需要分別爲所有可能的'T'調用'SetLambdaFor'嗎?我明白,他希望有一個相同的功能,按照「T」設置不同的行爲。 –

+0

我同意Wiktor。我需要「普遍」指定一個lambda模板用於各種類型,而不是針對每種類型。 –

+0

雖然不是lambda,但可以使用靜態類(或接口)和泛型靜態方法。在此代碼:https:// stackoverflow。com/a/45311276/6655648 –