5

只是好奇:当然,我们都知道泛型类型推断的一般情况是不确定的。所以 C# 根本不会做任何类型的子类型:如果Foo<T>是通用的,Foo<int>不是Foo<T>Foo<Object>或其他任何你可能的子类型做饭。当然,我们都用丑陋的接口或抽象类定义来解决这个问题。

但是...如果您无法解决一般问题,为什么不将解决方案限制在简单的情况下。例如,在我上面的列表中,很明显Foo<int>Foo<T>的子类型,检查起来很简单。与检查Foo<Object>相同。

如果他们只是说,哎呀,我们会尽力而为,是否还有其他深渊会从深渊中蔓延开来?或者这只是微软语言人员的某种宗教纯洁性?


更新:

这是一个非常古老的线程。这些天来,C# 有 var,它解决了我抱怨的一半,然后使用匿名委托的 Linq 风格,有一个很好的符号,不需要输入两次相同的东西。因此,我反对的每个方面都通过最近对 C# 的更改得到了解决(或者也许我只是花了一段时间才了解发布线程时刚刚介绍的东西......)我使用这些新的现在在 Isis2 系统中具有可靠的云计算 (isis2.codeplex.com) 的功能,我认为该库因此具有非常干净的外观和感觉。检查出来,让我知道你的想法)。——肯·伯曼(2014 年 7 月)

4

6 回答 6

7

他们已经解决了许多“简单”的情况:C# 4.0 支持接口和委托中泛型类型参数的协变和逆变。但不幸的是,不是上课。

解决此限制相当容易:

List<Foo> foos = bars.Select(bar => (Foo)bar).ToList();
于 2010-12-21T20:33:30.747 回答
5

它是显而易见的,它Foo<int>Foo<T>

也许对你来说,但对我来说不是。

对我来说,这对类型系统造成的巨大漏洞根本无法接受。如果您想像这样将类型安全抛到一边,我宁愿使用实际上为这些东西设计的动态类型语言。

数组是协变的,尽管众所周知这会破坏类型安全,但已经够糟糕了,现在你想为所有事情都破坏它吗?

这触及了类型系统的核心。类型系统所做的只是拒绝程序。由于赖斯定理,那些被拒绝的程序包括完美类型的、类型安全的程序。

这是一个巨大的成本。带走表达能力,阻止我编写有用的程序。为了证明这个成本是合理的,类型系统更好地回报了很多时间。它基本上有两种方式来做到这一点:在程序级别和类型安全中恢复它所带走的类型级别的表现力。

前者被淘汰了,仅仅是因为 C# 的类型系统不够强大,无法让我表达任何有趣的东西。这只剩下后者,由于null协变数组、无限制的副作用unsafe等等,它已经处于相当不稳定的基础上。通过使泛​​型类型自动协变,您或多或少地完全消除了剩下的最后一点类型安全性。

只有极少数情况下S <: T ⇒ G<S> <: G<T>实际上是类型安全的。(IEnumerable就是一个这样的例子。)并且可能有同样多的情况只有S <: T ⇒ G<T> <: G<S>类型安全。( IObservable, IComparer, IComparable, IEqualityComparer.) 通常,既不 G<S> <: G<T>是类型安全的,也不G<T> <: G<S>是类型安全的。

于 2010-12-21T21:04:17.537 回答
2

关键是你不能对所有情况都这样做,所以你不要对任何情况都这样做。你在哪里画线是问题。如果你不为任何使用 C# 的人都知道它不会那样做。如果你只做一部分时间,那就是它变得复杂的时候。它可以成为关于您的代码将如何表现的猜谜游戏。对于程序员来说,简单和不简单的所有边缘情况都变得复杂,并且可能导致代码错误。

这是一个绝对会造成破坏的场景。假设您可以在场景 A 中推断 boo 是 bar。其他人来并更改了部分基本类型,这不再适用。通过使其始终适用或从不适用,您永远不会遇到这种情况。在复杂的环境中,追踪这样的问题绝对是一场噩梦,尤其是当您在编译期间(反射、DLR 等)可能无法捕捉到这一点时。编写代码以预先手动处理转换要容易得多,而不是假设它会在您的场景中工作,而有时它可能不会(不包括升级到新版本)。

C# 4.0 确实解决了其中的一些问题,因为它们允许推断他们认为对程序员来说是“安全”的东西。

于 2010-12-21T20:33:39.250 回答
0

因此,我为我的真正问题找到了一个优雅的解决方法(尽管我确实发现 C# 过于约束)。

正如您所收集的,我的主要目标是编写代码,可以注册一个回调到您稍后将编写的方法(例如,在 2011 年),并且使用今天不存在的泛型类型参数在外部定义,因此在我写我的图书馆时(今天)我无法使用。

例如,我需要制作一个您将在明年定义的对象列表,并遍历列表中的元素,回调每个对象的“Aggregate<KeyType, ValueType>”方法。我的沮丧是,虽然你当然可以注册你的类或类中的一个对象,但 C# 类型检查并没有让我调用这些方法,因为我不知道类型(我可以通过 GetType() 获取类型,但可以'不要将它们用作表达式中的类型)。所以我开始使用反射来手动编译所需的行为,这很丑陋并且可能违反类型安全。

答案是……匿名类。由于匿名类就像一个闭包,我可以在“注册”例程中定义我的回调(它可以访问类型:您可以在注册对象时传递它们)。我将创建一个小的匿名回调方法,直接内联,并保存一个委托给它。回调方法可以调用您的聚合器,因为 C# 认为它“知道”参数类型。然而我的列表可以在我编译我的库时列出具有已知类型签名的方法,即明天。

非常干净,C# 最终不会让我发疯。尽管如此,C# 确实没有尽力推断子类型关系(抱歉 Eric:“什么应该是子类型关系”)。泛型不是 C 中的宏。然而,C# 太接近于将它们当作它们是!

这回答了我自己的(真正的)问题。

于 2010-12-22T23:09:58.393 回答
0

由于泛型类的实例具有不同的方法签名,我不相信考虑从基类继承特定类会有任何意义。

class Base<T> 
{
 public T Method() { return default(T);}
}

如果您建议 Base 和 Base 比它们都具有具有相同签名(来自基类)的“方法”,并且可以通过使用对基类的引用来调用它。Base<int>如果一个指向或Base<string>指向基类的对象,您能否解释一下您的系统中的明显返回值“方法” ?

于 2010-12-22T04:24:24.880 回答
0

一个非常好的现实生活中的例子是英语,这种语言在特殊情况下变得混乱并变得比必要的更复杂。在编程语言中,诸如“i 在 e 之前,除了 c 之后”之类的东西使该语言更难使用,因此用处不大。

于 2010-12-21T20:50:40.333 回答