假设我们有一个编译成.Net的编译器,其中所有类型(参数和本地)的所有方法都是通用的,并且在编写方法时不会指定类型,而是指定约束。
编译时,方法可能如下所示:
interface IFoo<T> {
T Foo();
}
interface IBar<TParam, TResult> {
TResult Bar(TParam param);
}
public TResult FooBar<TItem, TParam, TIntermediate, TResult>(A item, B param)
where TItem: IFoo<TIntermediate>
where TIntermediate: IBar<TParam, TResult> {
return item.Foo().Bar(param);
}
它将具有针对此的语法,并且将推断出类型,例如
FooBar = (item [I], param [P]) [I: IFoo<X>, X: IBar<P, R>] => [R] {
item.Foo().Bar(param)
}
大多数方法都是这样编译的程序(除了可以从使用中推断出确切类型的程序)是使用 .Net的高级通用类型系统还是滥用它?
例如,这样的事情是否会减慢CLR,因为它有太多的jitted类型,或者它是否“正常工作”,与普通代码一样有效?
答案 0 :(得分:2)
组合泛型类型约束的过程存在一个主要问题:没有通用的方法来对一个对象进行类型转换,以便可以将它传递给这样的例程 - 这种情况与没有这种类型参数的例程非常不同。
如果例程需要例如一个IFoo,我想传递一些我认为是IFoo的对象,我可以将对象转换为IFoo并将其传递给例程。如果对象可能是一个IFoo,编译器将生成代码,假定该对象是一个IFoo,如果没有则失败。
如果例程接受泛型类型参数,则传入的对象必须是编译时类型,它满足所有约束。如果只有一个约束并且它是特定的基本类型或接口(例如IFoo),那很容易 - 将其强制转换为约束隐含的类型。类型转换后的对象的编译类型将满足约束,如果对象的运行时类型满足约束,则转换将成功。
然而,如果想要调用其参数被约束为实现多个约束的例程Wowzo,则会出现困难,例如,接口IFoo和基类Wuzzle,但是想要传递给该例程的对象不共享满足约束的任何公共基类型。例如,假设类Wuzzle1,Wuzzle2和Wuzzle3都实现了IFoo,并且都继承自Wuzzle,但是Wuzzle没有实现IFoo。我有一系列的Wuzzle,其中的物品是Wuzzle1,Wuzzle2和Wuzzle3的混合物。如果一个数组元素碰巧是一个Wuzzle1,我可以将它转换为Wuzzle1并将其传递给Wowzo。如果它恰好是一个Wuzzle2,我可以把它投到一个Wuzzle2并传递给Wowzo。与Wuzzle3一样。不幸的是,没有很好的“通用”方法将数组元素传递给Wowzo,而不必明确处理列表中可能存在的每种可能的类型。答案 1 :(得分:2)
简答:总是使用通用约束是在标准多态性面前不必要使用泛型,因此算作滥用我书中的特写。我无法谈论潜在的大量通用实例的实际性能影响,但如果没有任何好处,我不会冒险。
长答案:
对于提议的新语言,您可以自由地使用语法,但理想情况下,编译器应尝试使实际编译的方法尽可能不通用。这改善了导入库的C#/ VB / etc开发人员的体验,因为我不认为通用约束在Intellisense中表现良好。这也减少了运行时必须进行的通用实例化的数量。但是,制作该算法可能会非常困难。例如,我最初认为您的示例方法可以像这样重写:
TResult FooBar<TParam, TResult>(IFoo<IBar<TParam, TResult>> item, TParam param)
{
return item.Foo().Bar(param);
}
这很接近,但由于IFoo
上没有差异以及结构可能实现IBar
的可能性,因此不太相同,在这种情况下,方差仍然无济于事。
另一方面,发布泛型方法的不好时间的一个例子是:
Write = (stream [S], data [D]) [S: Stream, D: byte()] => [] {
stream.Write(data, 0, data.Length)
}
这里的直接翻译可能会产生:
public void Write<S, D>(S stream, D data) where S : Stream, D : byte[]
{ stream.Write(data, 0, data.Length); }
由于标准多态性,因此不需要任何通用参数,而是可以发出此参数:
public void Write(Stream stream, byte[] data)
{ stream.Write(data, 0, data.Length); }
答案 2 :(得分:1)
一个大问题是方法重载。 CLR不认为这些方法不同。
public void Method<T>(T arg){} where T:IFoo
public void Method<T>(T arg){} where T:IBaz
不可否认,您可以使用modreq
/ modopt
参数修饰符以您的语言解决此问题。但是,这意味着信息从大多数其他.NET语言中丢失给调用者,或者根本无法被其他语言调用。
另一个痛点是虚拟方法的性能下降。虚拟通用调度比正常调度慢一个数量级。
最后,确实没有解决约束组合问题的好方法,你必须在jit时间之前发出一个类型来满足类型系统,如isinst
和castclass
这样的指令上班。因此,如果您想支持x as IFoo,IBar
,则必须创建<GeneratedInterface>_IFoo_IBaz:IFoo,IBaz
的界面。然后x的实际类型必须添加对生成的接口的支持,因为.NET实际上没有鸭子类型。
你也可能会陷入神奇的痛苦x as IFoo<T>,IBaz<T>
但是T这是一个不可能的组合,(例如,第一种情况下的T是结构,第二种情况下的T是类(x实际上是{{1}创建这样一种语言可能非常简洁但很难。
答案 3 :(得分:0)
如果来自这种编程语言的结果IL是可验证的,那么它不会滥用类型系统,因为它旨在运行可验证的所有内容(以及一些无法验证的内容)。
我的问题是 - 你为什么要这样做?使用多态性处理的静态类型方法参数有什么问题?