C#类型转换泛型(协变和逆变)?

9
我需要在这方面一些建议/帮助,我已经看不出树木和森林的区别了。
这是一系列直接实现某些使用通用类型的接口的类。
然后,我试图将具体类型进行转换,例如:
MyGenericObject<SomeObject> _obj;

IMyGenericObject<ISomeObject> _genObj = (IMyGenericObject<ISomeObject>)_obj;

// 无效的转换

我读过一些有关协变和逆变的文章,但是并不太清楚为什么这样做是不可能的,或者如何解决它?


所以,在这个例子中:

public interface IMyObject<in T> where T : IBaseObject
{
    T Activity { get; set; }
}

无法工作,因为您无法获取和设置Activity属性。

在这个例子中,我需要执行以下操作:

public interface IMyObject<out T> where T : IBaseObject
    {
        T Activity { get; }
    }

希望这能对某些人有所帮助,感谢所有提供帮助的人!

请展示您接口的定义。 - Daniel Hilgarth
2个回答

12
只有在将接口声明为具有协变(out)参数时才能执行此操作。如果参数以协变方式使用,则只能这样做。
例如,如果接口IMyGenericObject<T>具有接受T参数的方法,则会阻止您将参数声明为协变。相反,如果存在返回T的方法,则会阻止您将参数声明为逆变。
编辑
回应您在SLaks答案中的评论,我倾向于重复Eric Lippert曾经写过的关于协变和逆变的所有内容。请参见http://blogs.msdn.com/b/ericlippert/archive/tags/Covariance+and+Contravariance/以及他在SO中的答案(最近的是https://dev59.com/713Va4cB1Zd3GeqPFO9r#8380213)。
总结如下:
无法将IList<string>强制转换为IList<object>,因为可以将FileInfo传递给IList<object>,但不能将其传递给IList<string>
无法将IList<object>强制转换为IList<string>,因为可以从IList<string>检索项目并将其分配给字符串引用,但是IList<object>可能包含FileInfo,它无法分配给字符串引用。
编辑2
由于您要求建议,因此还可以将接口拆分为协变和逆变部分。继续使用列表示例,您可以拥有以下接口:
public interface ICovariantList<out T>
{
    T this[int index] { get; }
    //...
}

public interface IContravariantList<in T>
{
    T this[int index] { set; }
    void Add(T item);
    //...
}

public class SomeList<T> : ICovariantList<T>, IContravariantList<T>
{
    //...
}

这使你可以根据上下文以协变或逆变的方式使用该类。

谢谢,我有点明白了,尽管最后我并不需要setter...但是下次我会知道的! - sambomartin

5
您需要将接口声明为具有协变(out)泛型参数。

4
协变是“输出”的意思;而逆变是“输入”的意思。助记符:“o”代表协变和输出;“n”代表逆变和输入。 - phoog
好的,谢谢。我接下来遇到的问题是编译器报告:“错误1:无效的差异:类型参数'xxx'必须在'xxx'上不变地有效。”根据我所读的,我需要使接口只读/不变。我有一个IMyGenericInterface的属性SomeObject SomeObjectInstance {get;set;} 有什么想法吗? - sambomartin
@sambomartin:你需要了解方差是如何工作的。可写变量属性本质上是不安全的类型。 - SLaks
@SLaks - 你说得对,我确实这么做了 - 但是我是通过一个最初看起来很简单的例子来学习的。从phoog所说的 - 因为我的接口有一个返回T的方法,这就阻止我将参数声明为逆变(in)? - sambomartin
2
@SLaks:一个可写的协变属性不是类型安全的。一个可写的逆变属性正是逆变的全部意义所在。 - phoog
我不明白为什么这个答案会得到那么多赞同的理由。 - Rahul

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接