为什么这会导致错误CS4011:无法从使用中推断出类型参数?

时间:2016-07-22 06:13:17

标签: c# generics type-inference

我有以下样本。

  class Program {
    static void Main(string[] args) {
      var varc = new C();
      // Error CS0411
      var varf1b = varc.F1(t => new { City = t.City }, (t, v) => v + t.Status, (g, a) => new { City = g.City, Bigstatus = a });
      // compiles OK
      var varf1a = varc.F1(t => new { City = t.City }, (C t, int v) => v + t.Status, (g, a) => new { City = g.City, Bigstatus = a });

      // now replace varc by an anonymous class -- how to solve it now?
      var varanon = new { };

    }
  }

  public class C {
    public string City;
    public int Status;
  }

  public static class X {
    public static T1 F1<T2, T3, T4, T1>(
      this T2 s,
      Func<T2, T3> g,
      Func<T2, T4, T4> a,
      Func<T3, T4, T1> r)
      where T2 : class where T3 : class where T1 : class {
      return null;
    }
  }

第一个呼叫触发错误CS0411。我不能用通常的方法来解决这个问题,因为它使用了匿名类型。幸运的是,向其中一个lambda添加类型似乎足以让编译器满意。

为什么?具体是什么使第一个例子失败而第二个例子成功?

其次,是否有任何方法可以编写函数调用,这样就不会发生这种情况,并且用户不必面对必须在lambda中插入类型?

是的,其他人也提出了类似的问题,但这里的独特之处在于:(a)使用匿名类型(b)通过向lambda添加类型来修复。

编辑:给出的解决方案的问题是它不能与匿名类一起使用,因为没有可能的类型注释。

2 个答案:

答案 0 :(得分:1)

问题是:v的类型是什么?

编译器不会从用作输出的表达式推断输入的类型。编译器使用输入的推断类型来为输出编写有效的表达式。因此,(t, v) => 1并不意味着v的类型为int

这是编译器试图分析您的第一次尝试:

var varf1b = varc.F1(t => new { City = t.City }, (t, v) => v + t.Status, (g, a) => new { City = g.City, Bigstatus = a });          
                     |         |                  |  |                    |  |                         |
                     |         |                  |  |                    |  |                         |
              T2: C (varc)     |                  |  ?                    |  ?                         |
                               |                  |                       |                  T1: new { string City,  ? Bigstatus }
                      T3: new { string City }     |                       |
                                                 T2: C                    |
                                                                    T3: new { string City }

这是编译器试图分析你的第二次尝试:

var varf1b = varc.F1(t => new { City = t.City }, (C t, int v) => v + t.Status, (g, a) => new { City = g.City, Bigstatus = a });          
                     |         |                    |      |                    |  |                         |
                     |         |                    |      |                    |  |                         |
              T2: C (varc)     |                    |    T4: int                |  T4: int                   |
                               |                    |                           |                  T1: new { string City,  int Bigstatus }
                      T3: new { string City }       |                           |
                                                   T2: C                        |
                                                                          T3: new { string City }

所以varf1b的类型为:T1: new { string City, int Bigstatus },推理成功。

一个更简单的例子:

private static void GenericMethod<T1>(Func<T1, T1> func)
{
    // ...
}

GenericMethod((a) => 1); // CS0411, `a` is not inferred from the body of the method.

GenericMethod<int>((a) => 1); // compiles

GenericMethod((int a) => 1); // compiles

答案 1 :(得分:-1)

我现在看到的答案是编译器没有可用于确定T4类型的信息。特别是,它无法检查表达式并从中推断出类型。

因此,为了解决这个问题,我们需要一个明确设置T4类型的参数。这是一种方法。

  var varf1c = varc.F2(t => new { City = t.City }, 0, (t, v) => v + t.Status, (g, a) => new { City = g.City, Bigstatus = a });

public static T1 F2<T2, T3, T4, T1>(
  this T2 s,
  Func<T2, T3> g,
  T4 i,
  Func<T2, T4, T4> a,
  Func<T3, T4, T1> r)
  where T2 : class where T3 : class where T1 : class {
  return null;
}

现在T4与文字参数0相关联,编译器可以从中推断出int的类型。