【发布时间】:2016-07-20 16:59:30
【问题描述】:
考虑具有协变类型T 的接口。我正在研究使用T 的该接口的所有派生类中的属性都是只读的,如果是泛型类则为协变的情况。假设这个接口定义了一个使用T 作为参数类型的方法。它允许哪些违规行为?
例如,考虑:
interface ICov<out T> {
void maybe_safe_set(T v);
}
class ImplCov<T> : ICov<T> {
public readonly T a;
public readonly IEnumerable<T> b;
public readonly IEnumerable<IEnumerable<T>> c;
// public readonly IList<T> d; // but not this
public void maybe_safe_set(T v) {
// do things that can't modify state: the type of our
// readonly, covariant IEnumerable members can't be modified
}
}
在 C# 中,我收到错误:
无效方差:类型参数“T”必须在“ConsoleApplication.ICov.maybe_safe_set(T)”上逆变有效。 'T' 是协变的。
这并不奇怪,因为T 处于逆变位置。但是,我想不出这里可能发生的违规行为。
【问题讨论】:
-
协变意味着类型只从接口消费,从不传递给它。这样想,
out关键字意味着该类型应该只从接口中出来,但是您将它作为maybe_safe_set方法的参数输入。T可以改为定义为逆变式。接口是一个契约,它不知道给定的实现可能会做什么。 -
public static class C<T> { public static T Value }和public void maybe_safe_set(T v) { C<T>.Value = v }和((ICov<object>)new ImplCov<string>()).maybe_safe_set(new object())。现在您尝试将object分配给C<string>.Value类型为string。 -
如果允许,您可以将
ImpleConv<string>转换为ICov<object>,然后您可以调用maybe_safe_set并传递不是string的东西,然后您将有一个运行时类型错误。所以实际上它与实现类的实现细节没有任何关系。 -
你尝试的时候发生了什么?简单地尝试它不能回答你的问题吗?这里的评论解释了为什么将
T的值传入maybe_safe_set()方法对于out T类型参数是非法的。协方差/逆变方差的全部意义在于,当您滥用泛型类型时,编译器会发出错误。因此,如果您遇到错误,则说明您在滥用它。如果“举手”是指“产生编译器错误”,您应该这样说,提供错误的确切文本,并解释您对错误的不理解。 -
@juharr 这是一个很好的观点。如果没有静态类,我也想不出可以作用于特定类型的实现,但 PetSerAl 的评论也是一个完美的例子。
标签: c# covariance