泛型不能推断第二个参数

本文关键字:第二个 参数 不能 泛型 | 更新日期: 2023-09-27 18:03:19

我注意到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

但是如果我使用一个类型,我不需要显式地指定类型:

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#是这样设计的呢?不允许显式声明只有一个参数的语言实现有什么不同?

泛型不能推断第二个参数

c#被设计成一种比c++稍微不那么费力的语言。

特别是,我不认为将c#泛型与c++模板进行比较是一个好主意,原因有很多——在某些情况下,它们基本上是两种完全不同的方法来完成类似的事情。c++方法在某些方面当然是灵活的——尽管它不允许(据我所知)只以二进制形式存在的模板,或者在执行时创建新的模板专门化。基本上,c++模板方法与。net的其他组合方式不能很好地配合在一起。 至于为什么你不能指定一些类型参数并允许其他参数被推断(这是语言决定而不是平台决定;我确信,就。net本身而言,这是可行的)——再次强调,我相信这是为了简单起见。选择正确的方法正确的类型参数在c#中已经非常复杂了——比大多数c#开发人员所能理解的还要复杂。它包括:
  • 可能考虑从目标的编译时类型向上的类型层次结构的方法
  • 按参数数重载
  • 类型参数个数进行重载
  • 命名参数的效果
  • 可选参数的效果
  • 泛型参数约束对参数类型的影响(注意,不是目标方法指定的约束)
  • 委派转换的方法组
  • 匿名函数转换
  • 类型参数的类型推断
  • 动态类型
  • 一般协方差和逆变性
就我个人而言,我认为这足以让我理解,而不是允许有更多的可能性,通过"如果M具有至少与指定类型参数一样多的类型参数,则M仍然可以是候选的"。还需要命名类型参数和可选类型参数吗?;)

我已经研究了重载很多,完全遵循规范等。我发现了一些让语言设计者挠头的地方,并试图找出编译器应该做什么。我发现了编译器肯定出错的地方。如果没有很好的理由,我不想在这里增加任何复杂性。

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

  • 该特性对终端开发人员的好处
  • 终端开发人员了解该功能所花费的时间成本
  • 语言设计者彻底设计和指定它的成本
  • 编译器编写者正确实现它的成本
  • 测试团队彻底测试它的成本(与其他有关过载的一切一起)
  • 未来潜在功能的成本(如果这个功能使语言更复杂,那么其他功能的"潜在的"额外复杂性就会减少)

正如Dan所说,c#不允许您仅推断泛型参数集的某些类型参数。这很可能使基于泛型参数数量的重载成为可能(这是c#允许的,至少对于泛型类来说)。

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

在想要指定某些类型参数并推断其他类型参数的某些情况下,有一种方法可以提供帮助,那就是创建一个带有想要指定的参数的泛型静态类,然后在该类中创建一个带有想要推断的参数的泛型静态方法。例如,我有一个方法,给定一个可转换为Action(T,U,V)和T的方法,它将生成一个Action(U,V),它将调用带有原始指定的T以及U和V的委托。该方法将被调用为(vb语法):

<>之前NewAction = ActionOf(FooType, BarType)。NewAction(addressofmyfunctionofbozfoobar, someBoz)之前

编译器可以使用someBoz的类型来确定一个泛型类型参数,尽管它需要显式指定FooType和BarType参数。