代码之家  ›  专栏  ›  技术社区  ›  Cheeso

.NET:在静态方法上推断的泛型类型

  •  14
  • Cheeso  · 技术社区  · 14 年前

    假设我有

    public static List<T2> Map<T,T2>(List<T> inputs, Func<T, T2> f)
    {
        return inputs.ConvertAll((x) => f(x));
    }
    
    private int Square(int x) { return x*x; }
    
    public void Run()
    {
        var inputs = new List<Int32>(new int[]{2,4,8,16,32,64,128,256,512,1024,2048});
    
        // this does not compile
        var outputs = Map(inputs, Square); 
    
        // this is fine
        var outputs2 = Map<Int32,Int32>(inputs, Square);
    
        // this is also fine (thanks, Jason)
        var outputs2 = Map<Int32,Int32>(inputs, (x)=>x*x);
    
        // also fine
        var outputs2 = Map(inputs, (x)=>x*x);
    }
    

    为什么不编译?

    编辑 :错误为:

    错误CS0411:无法根据用法推断方法“namespace.map<t,t2>(system.collections.generic.list<t>,system.func<t,t2>)”的类型参数。尝试显式指定类型参数。

    为什么必须指定map()函数的类型?它不能从过去推断出这个吗? Func<T> ?(在我的例子中,正方形)


    答案和……一样吗?
    C# 3.0 generic type inference - passing a delegate as a function parameter ?

    5 回复  |  直到 14 年前
        1
  •  10
  •   jason    14 年前

    从您的错误消息:

    方法“”的类型参数 [...].Map<T,T2>(System.Collections.Generic.List<T>, System.Func<T,T2>) 无法从用法推断“”。尝试显式指定类型参数。

    请注意,错误消息指出它无法计算出类型参数。也就是说,它在解析一个类型参数时遇到问题 T T2 . 这是因为规范的第25.6.4条(类型参数的推断)。这是规范中涉及推断泛型类型参数的部分。

    如果以下任何一项为真,则不会从参数推断任何内容(但类型推断成功):

    […]

    参数是方法组。

    因此,编译器无法使用 Square 推断 T2 . 请注意,如果您将声明更改为

    public static List<T> Map<T>(List<T> inputs, Func<T, T> f) {
            return inputs.ConvertAll((x) => f(x));
    }
    

    然后

    var outputs = Map(inputs, Square);
    

    是合法的。在这种情况下,它已经解决了 T int 从事实上 inputs 是一个 List<int> .

    现在,更深层的问题是,为什么上面的规范?也就是说,为什么方法组在类型参数解析中不起作用?我想是因为这样的情况:

    class Program {
        public static T M<T>(Func<T, T> f) {
            return default(T);
        }
    
        public static int F(int i) {
            return i;
        }
    
        public static float F(float f) {
            return f;
        }
    
        static void Main(string[] args) {
            M(F); // which F am I?
        }
    }
    
        2
  •  2
  •   Dathan    14 年前

    推理无法推断委托的类型,而不是列表:

    // this is also fine
    var outputs3 = Map(inputs, new Func<int, int>(Square));
    
    // more calls that compile correctly
    var outputs4 = Map(inputs, x => Square(x));
    
    var outputs5 = Map(inputs, x => x * x);
    
    Func<int, int> t = Square;
    var outputs6 = Map(inputs, t);
    

    不过,我不知道为什么-也许没有从 Square Func<Int32, Int32> 是吗?奇怪的是 Func<int, int> t = Square; 是有效的,但编译器不能自己进行跳跃…也许是虫子?

        3
  •  1
  •   Eric Mickelsen    14 年前

    经过一番挖掘,我发现你对另一个答案的怀疑是正确的。C 3.0规格说明如下:

    7.4.2.1-对于每个方法参数,ei:如果ei是匿名的 函数或方法组,显式的 参数类型推断(7.4.2.7)为 制造的。。。 7.4.2.7-…如果e是一个显式类型的匿名函数, 参数类型U1_uk和T是 具有参数类型的委托类型 v1_vk然后针对每个用户界面 推论(_§7.4.2.8)由用户界面作出 对于相应的vi。

    换句话说,匿名方法和方法组(哪个方是)只能推断参数类型 明确地 . 我认为你提到的答案末尾的理由很好地概括了这一点。因为不能总是从方法组中隐式地进行类型推断,所以编译器甚至不会根据规范尝试这样做。

        4
  •  1
  •   Rex M    14 年前

    这不起作用的原因是C要对一个方法进行类型推断,它必须知道转换的另一端的委托类型。但此时,目标委托类型仍然不是完全已知的-只有t(in t)是已知的,t2仍然是未解析的。

    Func<int, int> f = Square;
    //works because we provided the destination type
    //of the conversion from Square to delegate
    
    Map(inputs, i => Square(i));
    //works because the lambda follows the actual method call
    //and determines its own return type
    
        5
  •  0
  •   John Saunders KB22    14 年前

    以下也可以,我不知道为什么:

    var outputs = Map(inputs, i => Square(i));