我有以下类:
X : BaseX
XView : BaseView<X>
XDAO : BaseDAO<X>
MainViewModel<TX, TView, TDAO>
where TX : X
where TView : View<X>
where TDAO : DAO<X>
XMainViewModel : MainViewModel<X, XView, XDAO>
我有一个XMainViewModel<X, XView, XDAO>
的实例。我想将此转换为MainViewModel<BaseX, BaseView<X>, BaseDAO<X>>
。这可能吗?它可能涉及协变。我不熟悉协变,不久前才开始使用泛型,所以我在这里很困惑(我也希望我在这个问题上没有错别字)。感谢您的帮助。
这可能吗?
否。通用协方差和逆变差仅在使用引用类型构造的接口和委托上受支持。
我在这里很困惑
这是一个令人困惑的话题。思考它的一种方法是总是问自己"假设这是合法的;会出什么问题?"你说
我有
XMainViewModel<X, XView, XDAO>
。我想将此转换为MainViewModel<BaseX, BaseView<X>, BaseDAO<X>>
让我们看一个更简单的例子。您有一个List<Giraffe>
,并且希望将其转换为List<Animal>
。会出什么问题?
List<Giraffe> giraffes = new List<Giraffe>();
giraffes.Add(new Giraffe());
List<Animal> animals = (List<Animal>)giraffes; // This is illegal. What if it were legal?
animals.Add(new Tiger());
Giraffe g = giraffes[1]; // And we just put a Tiger into a variable of type Giraffe.
这就是为什么这是非法的,也是为什么你的例子也是非法的。但这是合法的:
IEnumerable<Animal> animals = giraffes;
为什么这是合法的?因为IEnumerable根本无法插入老虎。接口已被标记为协方差安全,C#编译器已验证它是安全的,因此允许进行此转换。
我已经写了很多关于协方差和反方差的SO答案、博客文章和文章,所以如果你想了解更多关于这个功能的信息,以及我们为什么这样设计它,请进行一些搜索。
您可以通过以下方式使您的示例合法:(1)使MainViewModel<A, B, C>
成为接口并将其标记为IMainViewModel<out A, out B, out C>
,然后(2)确保接口的任何方法都不将A、B或C作为参数,类型A、B和C的任何属性都没有setter,依此类推。也就是说,A、B、C的每次使用都是输出位置,而不是输入的位置。这就是编译器如何知道它对协方差是安全的。
我也希望我没有在问题中拼写错误
这是"协方差",而不是"协变量"。"协方差"是一个名词;"协变"是一个形容词。