仅对lambda表达式的隐式类型推断?为什么?困惑!

时间:2009-10-18 12:55:31

标签: c# .net .net-3.5 lambda

我有以下示例代码(仅用于C#3.5学习目的!)。

我正在调用接受IEnumerable和sort函数的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);
}

4 个答案:

答案 0 :(得分:6)

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

选项:

  • 从lambda表达式中调用方法,如图所示
  • 明确指定类型参数,而不是依赖于推理
  • 将方法组强制转换为所涉及的特定类型(比第二个选项更差)
  • 使用单独的局部变量并明确指定其类型

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

答案 1 :(得分:6)

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

这可能是一个更全面的解释:

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

答案 2 :(得分: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&gt;是一个委托类型,它意味着它需要一个“指向函数的指针”,但不是函数。其他地方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什么都没有,它是一个方法名,它不是任何类型或实例。方法参数只能有某种东西是某种类型的实例。 new Delegate是方法指针的实例,而不是任何方法。

x => SortInt(x)是上述所有内容的缩写,lambda通常是编写匿名委托的较小形式,编译器不会将SortInt推断为x => SortInt(x)

答案 3 :(得分:0)

您的代码存在一些阻止其工作的问题。

首先,你的类型并不都匹配。你传递一个int []作为IEnumerable,但是如果不调用.AsEnumerable()就不能这样做。

第二,T和TResult,尽管在使用中是相同的,(int),但是对于编译器来说是不一样的,但是你传入一个int数组并且期望一个IEnumerable而不说结果的类型是什么。所以你必须用你的版本传递TResult的类型(比如Sort(intArray.AsEnumerable(),SortInt)),这是有效的,但你不需要TResult,因为你只是订购了相同类型的T. / p>

所以,我摆脱了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表达式时存在差异的原因的解释。