【问题标题】:C# Casting generics (covariance and contravariance?)C# 铸造泛型(协变和逆变?)
【发布时间】:2011-12-06 14:45:17
【问题描述】:

我需要一些建议/帮助,我再也看不到树上的木头了。

这是一系列使用泛型实现一些接口的简单类。

然后我尝试转换具体类型,例如:

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; }
    }

希望对某人有所帮助,感谢大家的帮助!

【问题讨论】:

  • 请显示你的接口定义

标签: c# .net c#-4.0


【解决方案1】:

只有将接口声明为具有协变 (out) 参数时,才能这样做。只有在协变使用参数时才能这样做。

例如,如果接口IMyGenericObject&lt;T&gt; 有一个采用T 参数的方法,这会阻止您将参数声明为协变。相反,如果有一个方法返回一个T,这会阻止您将参数声明为逆变。

编辑

作为对您对 SLaks 回答的评论的回应,我很想重复 Eric Lippert 曾经写过的关于协变和逆变的所有内容。请参阅http://blogs.msdn.com/b/ericlippert/archive/tags/Covariance+and+Contravariance/ 以及他在 SO 中的答案(最近是https://stackoverflow.com/a/8380213/385844

总结一下:

您不能将IList&lt;string&gt; 转换为IList&lt;object&gt;,因为将FileInfo 传递给IList&lt;object&gt; 是合法的,但将其传递给IList&lt;string&gt; 是不合法的。

您不能将IList&lt;object&gt; 强制转换为IList&lt;string&gt;,因为从IList&lt;string&gt; 检索项目并将其分配给字符串引用是合法的,但IList&lt;object&gt; 可能包含一个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>
{
    //...
}

这允许您根据上下文以协变或逆变的方式使用类。

【讨论】:

  • 谢谢,虽然我最终还是不需要二传手……但我知道下次!
【解决方案2】:

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

【讨论】:

  • 协变量是out;逆变是in。助记符:“o”表示协变和输出; "n" 表示逆变和 in。
  • 好的,谢谢。我遇到的下一个问题是编译器报告:“错误 1 ​​无效方差:类型参数 'xxx 必须在 'xxx 是协变的情况下始终有效。”根据我的阅读,我需要使界面只读/不变。我有一个 IMyGenericInterface SomeObject SomeObjectInstance {get;set;} 的属性有什么想法吗?
  • @sambomartin:您需要了解方差的工作原理。可写的变体属性本质上是非类型安全的。
  • @SLaks - 你是对的,我确实这样做了 - 但我正在通过一些最初看起来很简单的例子来学习这一点。从 phoog 所说的 - 因为我的接口有一个返回 T 的方法,这阻止了我将参数声明为逆变 (in)?
  • @SLaks:可写的 covariant 属性不是类型安全的。可写的逆变属性正是逆变的全部内容。
猜你喜欢
  • 1970-01-01
  • 2013-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多