2010-05-13 60 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#。