泛型不能推断第二个参数?

时间:2011-07-22 22:47:41

标签: c# .net templates generics

我注意到C#编译器没有推断出第二个通用参数 例如:

C ++模板代码:(我知道模板不像泛型一样工作)

class Test {
public:
template <class T,class V> 
    T test(V v) {
       //do something with v
       return T();
    }
};

int i = 0;
Test t = new Test();
double j = t.test<double>(i); //infers V as int

模板(和泛型)不能推断返回类型,所以在C ++中我给它第一个模板参数,第二个模板参数是从变量类型推断出来的。

现在,C#中的相同示例:

class Test {
    public T test<T,V>(V v) where T: new() {
       //do something with v
       return new T();
    }
};

int i = 0;
Test t = new Test();
double j = t.test<double>(i); //Error Using the generic method 'Test.test<T,V>(V)' requires '2' type arguments

但如果我使用1种类型,我不必明确指定类型:

class Test {
    public V test<V>(V v) where V: new() {
       return new V();
    }
};

int i = 0;
Test t = new Test();
int j = t.test(i); //OK infers V as int.

那么,为什么不能C#泛型推断出第二种类型(在c ++模板中它显然可以)? 我确信它的设计是这样的(我怀疑.Net团队忽略了这一点),所以为什么它是这样设计的,我必须明确指定这两种类型?

修改

从目前为止我们在答案中的讨论中,两种语言都支持按模板参数的数量进行重载。

再次,为什么是这样设计的C#?语言实现中有什么不同,不允许只显式声明一个参数?

3 个答案:

答案 0 :(得分:28)

C#被设计成一种比C ++略逊一筹的语言。

特别是,我不认为将C#泛型与C ++模板进行比较是出于各种原因的好主意 - 它们基本上是两种在中完成类似事物的完全不同的方法一些情况。 C ++方法在某些方面肯定是灵活的 - 虽然它不允许(据我所知)模板只存在于二进制形式,或者在执行时创建新的模板特化。基本上,C ++模板方法与.NET结合在一起的其余部分并不相符。

现在,为什么你不能指定某些类型的参数并允许推断其他参数(这是语言决定而不是平台决定;我确信就.NET本身而言,这是可行的) - 再次,我相信这是为了简单起见。选择正确的方法正确的类型参数在C#中已经非常复杂 - 比大多数C#开发人员更加复杂。它涉及:

  • 可能会从目标
  • 的编译时类型中考虑类型层次结构的方法
  • 按参数数量重载
  • 类型参数的数量重载
  • 命名参数的影响
  • 可选参数的影响
  • 泛型类型参数约束对参数类型的影响(不是目标方法指定的约束,请注意)
  • 委派转化的方法组
  • 匿名函数转换
  • 类型参数的类型推断
  • 动态类型
  • 通用协方差和逆变

就我个人而言,我认为这足以让我头脑发热,而不允许更多可能性通过“M仍然可以成为候选者,如果它至少 参数作为指定的类型参数“。您还想要命名类型参数和可选类型参数吗? ;)

我已经看了很多重载,完全遵循规范等等。我发现了让语言设计师抓住头脑并尝试弄清楚编译器应该做什么的区域。我找到了编译器肯定出错的区域。 我不想在没有充分理由的情况下增加任何复杂性。

所以是的,它基本上是为了简单起见,有时这很痛苦 - 但通常你可以解决它。对于每个潜在的功能,您需要考虑:

  • 该功能对最终开发人员的好处
  • 最终开发人员在理解它时花费的功能成本
  • 语言设计者在设计和指定时花费的成本
  • 编译器编写者正确实现它的成本
  • 测试团队在彻底测试时的成本(与重载的其他所有内容相结合)
  • 未来潜在功能的成本(如果这使得语言变得更复杂,那么其他功能的“可能更容易”的复杂性就会增加)

答案 1 :(得分:7)

正如Dan所说,C#不允许您仅推断通用参数集的某些类型参数。这可能会根据通用参数的数量启用重载(C#允许,至少对于泛型类)。

但是,您可以指定泛型类的参数,并推断该类中泛型方法的参数。但这种解决方法并不总是一个好的解决方案。

答案 2 :(得分:0)

有些事情可以帮助在某些情况下,人们想要指定一些类型参数并推断其他参数是创建一个带有想要指定的参数的通用静态类,然后在该类中有一个通用的静态方法想要推断出的参数。例如,我有一个方法,给定一个可转换为Action(T,U,V)的方法,以及一个T,将生成一个Action(U,V),它将使用最初指定的方式调用该委托T连同U和V.该方法将被调用为(vb语法):

  NewAction = ActionOf(Of FooType, BarType).NewAction(AddressOf MyFunctionOfBozFooBar, someBoz)

编译器可以使用someBoz的类型确定其中一个泛型类型参数,即使它需要明确指定FooType和BarType参数。