2009-10-18 41 views
4

我有以下示例代码(仅用于C#3.5学习目的!)。仅针对lambda表达式的隐式类型推断?为什么?困惑!

我正在调用接受IEnumerable和排序函数的Sort函数。如果我使用lambda表达式(情况A)调用它,编译器可以派生返回类型TResult,但是当我通过func SortInt(情况B)时,编译器会引发错误!

我无法理解为什么编译器不能在第二种情况下派生TResult!我似乎传递了完全相同的信息。还是那不准确?

请帮忙!

int[] intArray = { 1, 3, 2, 5, 1 }; 

IEnumerable<int> intArray2 = Sort(intArray, x => SortInt(x)); // <= CASE A - OK ! 

IEnumerable<int> nextIntArray = Sort(intArray, SortInt); // <= CASE B - Compile Error: Cannot Infer Type ! 

public static IEnumerable<TResult> Sort<T, TResult>(IEnumerable<T> toBeSorted,  
           Func<IEnumerable<T>, IEnumerable<TResult>> sortFunc) 
{ 
    return sortFunc(toBeSorted); 
} 

public static IEnumerable<int> SortInt(IEnumerable<int> array) 
{ 
    return array.OrderByDescending(x => x); 
} 
+0

你的名字就像是印度人 – 2009-11-09 10:28:27

回答

6

看起来好像在第二个示例中推理失败,因为编译器无法对SortInt执行重载解析。

这可以是一个更透彻的解释有用:

http://blogs.msdn.com/ericlippert/archive/2007/11/05/c-3-0-return-type-inference-does-not-work-on-member-groups.aspx

+0

那篇文章现在有些过时;基于这篇文章的反馈,我们确实最终改进了类型推断算法,这种算法在这种情况下有所改进。 – 2009-10-18 15:41:54

+0

这就是说,这仍然是我们在这种情况下无法做重载解决方案的一个例子。 – 2009-10-18 15:52:40

+0

@罗伯特,谢谢你!那篇文章恰恰处理了这一点! – Preets 2009-10-19 10:58:36

5

我同意这很烦人,类型推断不适用于方法组转换。我希望C#编译器在组中只有一个适用方法的常见情况下变得更聪明。但是,在复杂的情况下,最终可能会导致多种适用的重载,从而导致推断出不同的类型。

选项:

  • 呼叫从一个lambda表达式的方法,因为你已经证明
  • 指定类型参数明确,而不是依靠推理
  • 铸造方法组涉及到的特定类型的(比第二个选项更糟)
  • 使用一个单独的局部变量,并指定其类型明确

基本上我会坚持前两个选项中的任何一个,因为它们都是烦人的。请注意,第一个选项会有轻微的性能损失,因为它是一个额外的间接级别,但通常不会很重要。

+0

谢谢你的解释。我现在知道类型推断不适用于方法组!理解'为什么'虽然不是我最简单的任务! – Preets 2009-10-19 11:44:54

3
IEnumerable<int> intArray = Sort(intArray, x => SortInt(x)); // <= CASE A - OK ! 

在此声明,在x => SortInt(x)是一个代表表示如下...

delegate(IEnumerable<int> x){ 
    return SortInt(x); 
} 

从技术上讲,您不会在语句中的任何位置传递对SortInt的任何引用。

Func,IEnumerable>是一个委托类型,它意味着它需要一个“指向函数的指针”,但不是函数。 SortInt是一种方法。

如果你深入挖掘,你会发现x => SortInt(x)的实际编译时间表示如下...

private static IEnumerable<int> __XYZ(IEnumerable<int> x){ 
    return SortInt(x); 
} 

private delegate IEnumerable<int> __XYZHANDLER(IEnumerable<int> x); 

,你的1号线将

IEnumerable<int> intArray = Sort(intArray, new __XYZHANDLER(__XYZ)); 

现在看你的第2行,SortInt是什么,它的一个方法的名称,它不是任何类型或实例。只有一个方法参数可以拥有某种类型的某个实例。新的委托是方法指针的一个实例,而不是任何方法。编写器不会推断SortInt为x => SortInt(x)。

0

您的代码有几个问题妨碍了它的工作。

首先,你的类型并不都匹配。你正在传递一个int []作为IEnumerable,但是你不能在不调用.AsEnumerable()的情况下这样做。其次,T和TResult尽管在用法上相同(int),但与编译器不同,但是您传入一个int数组并期待IEnumerable,而不会说明结果的类型是。所以你必须传递TResult的类型(比如Sort(intArray.AsEnumerable(),SortInt)),它可以工作,但是你不需要TResult,因为你只需要命令相同的类型即T。

所以,我摆脱TResult的和固定的类型:

void Main() 
{ 
    var intArray = new [] { 1, 3, 2, 5, 1 }; 
    var ints = Sort(intArray.AsEnumerable(), x => SortInt(x)); 
    var nextInts = Sort(intArray.AsEnumerable(), SortInt); 
} 

public static IEnumerable<T> Sort<T>(
       IEnumerable<T> toBeSorted, 
       Func<IEnumerable<T>, IEnumerable<T>> sortFunc) 
{   
    return sortFunc(toBeSorted); 
} 

public static IOrderedEnumerable<T> SortInt<T>(IEnumerable<T> array) 
{  
    return array.OrderByDescending(x => x); 
} 

我喜欢上面的,但是,这是最接近我能得到你的样品,它的工作原理:

void Main() { 
    var intArray = new [] { 1, 3, 2, 5, 1 }; 
    var ints = Sort(intArray.AsEnumerable(), x => SortInt(x)); 
    var nextInts = Sort<int, int>(intArray.AsEnumerable(), SortInt); 
    } 

    public static IEnumerable<TResult> Sort<T, TResult>(
       IEnumerable<T> toBeSorted, 
       Func<IEnumerable<T>, IEnumerable<TResult>> sortFunc) 
    {  
     return sortFunc(toBeSorted); 
    } 
    public static IEnumerable<int> SortInt(IEnumerable<int> array){  
     return array.OrderByDescending(x => x); 
    } 

SortInt也可以是:

public static IOrderedEnumerable<T> SortInt<T>(IEnumerable<T> array){  
    return array.OrderByDescending(x => x); 
} 

Akash给出了使用lambda表达式时为什么会有差异的解释。