2010-05-13 28 views
8

有什麼辦法可以在此處概括類型定義? 理想情況下,我希望能夠改變'testInput'的類型,並在編譯時讓測試正確地推斷出類型。C#通用方法類型自變量推斷

public static void Run() 
{ 
    var testInput = 3; 
    var test = ((Func<int, int>) Identity).Compose<int,int,int>(n => n)(testInput); 
    Console.WriteLine(test); 
} 

public static Func<T, V> Compose<T, U, V>(this Func<U, V> f, Func<T, U> g) 
{ 
    return x => f(g(x)); 
} 

public static T Identity<T> (this T value) 
{ 
    return value; 
} 

更新: 我可以指定傳遞到撰寫該函數的類型,但是這仍然指定線路類型。

public static void Run() 
{ 
    var testInput = 3; 
    var identity = (Func<int, int>) Identity; 
    var test = identity.Compose((int n) => n)(testInput); 
    Console.WriteLine(test); 
} 

一點內容;我通過韋斯代爾的The Marvel of Monads

+0

你能提供更多關於你想達到的具體信息嗎?目前,您的Compose函數太鬆散地限制爲允許從lambda類型推斷T。它根本無法從使用中獲得足夠的信息。 – jeffora 2010-05-13 07:39:49

+0

更新了問題。 – CaptainCasey 2010-05-13 07:52:40

回答

3

嗯,因爲我正在閱讀文本,因此我今晚會發表自己的看法。我應該注意到,我並不是C#編譯器的專家,我沒有閱讀過規範(其中任何一個......對於任何事情),儘管你鏈接的那篇文章非常有趣,但如果我說我是任何類型的專家(或甚至全部理解它)。

注意事項之外,我拿你的問題是這樣的:

有沒有辦法,我可以在這裏概括 類型定義什麼辦法?

我認爲簡短的答案是否定的。根據提供的信息,C#編譯器的類型推斷部分根本沒有足夠的信息來從各種變量的使用中推斷出足夠的信息。

正如這裏的其他答案所示,它可以被簡化。您可以使用@ Lee的IdentityFunc來允許使用var identity進行類型推斷。但是,即使添加了這些內容,您的示例代碼仍然無法推斷出Compose的所有類型變量。

想象一下以下情況:

public static Func<T, V> Compose<T, U, V>(this Func<U, V> f, Func<T, U> g) 
{ 
    return x => f(g(x)); 
} 

public static T Identity<T> (this T value) 
{ 
    return value; 
} 

public static Func<T, T> IdentityFunc<T>(this T value) 
{ 
    return (Func<T, T>)Identity; 
} 

public static void Run() 
{ 
    var a = 3; // a is int 
    var i = a.IdentityFunc(); // i is Func<int, int>; 
    var test = i.Compose(n => n)(a) // test is expected to be int 
} 

最初,這可能會出現,如果test應該很容易推斷爲int。但是,返回類型i.Compose只能在事實之後從它的使用推斷出來。 C#編譯器顯然不會允許這樣做。

public static void Run() 
{ 
    var a = 3; // a is int 
    var i = a.IdentityFunc(); // i is Func<int, int>; 
    var c = i.Compose(n => n) // c is Func<T, int> - T cannot be resolved without knowledge of n 
    var test = c(a); // ideally have type inference infer c (Func<T, int>) as Func<int, int> 
} 

在這個例子中,在與ac使用編譯器將不得不追溯推斷調用的返回類型爲i.Compose<T, U, V>(n => n)Func<int, int>。這在C#編譯器中顯然是不可能的。拿掉電話c(a),編譯器將不知道使用c,這將消除推斷T(不是它可以)的任何可能性。有可能更高級的類型推斷系統能夠根據泛型返回的使用來進行這種推斷(可能是F# - 我不是專家的另一個主題)。由於Wes Dyer沒有提供這個特定例子的具體用法,目前還不清楚他是否使用了一些其他的魔法來允許你嘗試實現的類型推斷程度。

更多符合條件的人如Eric Lippert將能夠爲您提供更高水平的細節(以及技術準確度/敏銳度)。我讀到他在這裏寫的關於類型推斷問題的很好迴應,但我找不到它。他的blog有很多很棒的信息。如果你有興趣,你可以試着聯繫他。此外,他在這裏對這個問題的回答討論了monads(並最終鏈接到Wes Dyer的文章)購買你可能有興趣閱讀它:Monad in plain English? (For the OOP programmer with no FP background)

+2

其實我沒有太多補充到你的答案。正如您正確地注意到的,沒有lambda參數的類型,我們沒有足夠的信息來從Compose的調用站點推導出T。正如你所指出的,如果我們可以推遲分析直到使用合成結果,那麼我們可以取得更多進展,但我們不這樣做。 – 2010-05-14 15:33:27

+0

謝謝你對這個問題的出色答案。這個答案沒有明顯應得的票數。 – CaptainCasey 2010-06-17 02:24:10

2

工作你可以寫一個擴展方法返回一個類型標識功能:

public static Func<T, T> IdentityFunc<T>(this T value) 
{ 
    return (Func<T, T>)Identity; 
} 

(或只是return v => value;

您的測試就成爲

var testInput = 3; 
var identity = testInput.IdentityFunc(); 
test = identity.Compose((int n) => n)(testInput); 
1

我能得到的最接近的是爲n => n lambda顯式鍵入參數:

var test = ((Func<int, int>)Identity).Compose((int n) => n)(testInput); 
1

我不認爲你能達到你的理想; C#類型推斷不能像那樣工作。

您可能會喜歡F#。