为啥 C# (4.0) 不允许泛型类类型中的协变和逆变?
Posted
技术标签:
【中文标题】为啥 C# (4.0) 不允许泛型类类型中的协变和逆变?【英文标题】:Why does C# (4.0) not allow co- and contravariance in generic class types?为什么 C# (4.0) 不允许泛型类类型中的协变和逆变? 【发布时间】:2021-12-03 01:18:42 【问题描述】:造成这种限制的真正原因是什么?只是必须完成的工作吗?概念上很难吗?不可能吗?
当然,不能在字段中使用类型参数,因为它们始终是可读写的。但这不可能是答案,不是吗?
这个问题的原因是我正在写一篇关于 C# 4 中的方差支持的文章,我觉得我应该解释一下为什么它仅限于委托和接口。只是为了颠倒举证责任。
更新: 埃里克问了一个例子。
这个怎么样(不知道这是否有意义,但是:-))
public class Lookup<out T> where T : Animal
public T Find(string name)
Animal a = _cache.FindAnimalByName(name);
return a as T;
var findReptiles = new Lookup<Reptile>();
Lookup<Animal> findAnimals = findReptiles;
将它放在一个类中的原因可能是类本身中保存的缓存。并且请不要将您不同类型的宠物命名相同!
顺便说一句,这将我带到optional type parameters in C# 5.0 :-)
更新 2: 我没有声称 CLR 和 C# 应该允许这样做。只是试图了解导致它没有的原因。
【问题讨论】:
当然,这是一个合理的例子,但你没有展示任何不能用接口完成的东西。只需制作接口 ILookup首先,正如 Tomas 所说,CLR 不支持它。
其次,这将如何运作?假设你有
class C<out T>
... how are you planning on using T in here? ...
T 只能用于输出位置。正如您所注意到的,该类不能有任何类型为 T 的字段,因为可以写入该字段。该类不能有任何采用 T 的方法,因为这些是逻辑写入的。假设您有这个功能——您将如何利用它?
这对于不可变类很有用,例如,如果我们可以合法地拥有一个类型为 T 的只读字段;这样我们就大大减少了它被不正确写入的可能性。但是很难想出其他允许以类型安全方式变化的场景。
如果你有这样的场景,我很乐意看到它。这将有助于有朝一日在 CLR 中实现这一点。
更新:见
Why isn't there generic variance for classes in C# 4.0?
关于这个问题的更多信息。
【讨论】:
好问题 :) 我记得去年在 Lang.NET 上 Anders 和一些 Java 编译器极客(抱歉)之间的讨论,Java 专家要求这个特性。他似乎知道他为什么要问。但我不记得了。我想到了没有状态的类。我会尝试在问题中弥补一些东西。 这对于不可变数据类型(例如Tuple<T1, T2>
)来说绝对是一个不错的功能,但我同意这对于更改 CLR 来说不够令人信服:-)。
另一个用例是幻像类型,其中 T 仅用于类型安全,而不用于类的实现。
Lazy据我所知,CLR 不支持此功能,因此添加此功能也需要 CLR 方面的大量工作。我相信 CLR 在 4.0 版之前实际上支持接口和委托的协变和逆变,因此这是一个相对简单的实现扩展。
(不过,为类支持此功能肯定很有用!)
【讨论】:
你是对的。接口和委托的泛型类型参数的差异来自 CLR 2.0 - 只是在 C# 中没有【参考方案3】:如果它们被允许,则可以定义有用的 100% 类型安全(无内部类型转换)类或结构,如果它们的构造函数接受一个或多个 T 或 T 供应商,则它们的类型 T 是协变的。如果它们的构造函数接受一个或多个 T 消费者,则可以定义有用的 100% 类型安全的类或结构,这些类或结构相对于 T 是逆变的。除了使用“新”而不是使用静态工厂方法(很可能来自名称与接口名称相似的类)之外,我不确定类比接口有多大优势,但我可以当然可以看到使用不可变结构支持协方差的用例。
【讨论】:
以上是关于为啥 C# (4.0) 不允许泛型类类型中的协变和逆变?的主要内容,如果未能解决你的问题,请参考以下文章