【问题标题】:Casting to a generic class with interface使用接口转换为泛型类
【发布时间】:2015-01-13 22:42:18
【问题描述】:

根据 MarcinJuraszek 的意见更新

我觉得我在这里遇到了一个 co / contra 方差问题,但我不确定我是否了解如何解决它。我有这样的课:

public interface ISomeClass<TEnum, out S>
{
     TEnum Dim { get; }
     IEnumerable<S> Inc { get; }
}

public class SomeClass<TEnum, S> : ISomeClass<TEnum, S>
    where TEnum : struct, IConvertible
    where S : IMyInterface
{
    public TEnum Dim { get; set; }
    public IEnumerable<S> Inc { get; set; }
}

我有一个实现IMyInterface的类

public class MyImplementation : IMyInterface
{

}

当然,我有一个带有SomeClass 属性的类:

public class MyContainer<TEnum> where TEnum : struct, IConvertible
{
    public SomeClass<TEnum, IMyInterface> MyProp { get; set; }
}

现在我的问题是我无法将SomeClass&lt;MyEnum, MyImplementation&gt; 分配给MyProp 属性,因为我在运行时收到InvalidCastException 抱怨它无法将SomeClass&lt;MyEnum, MyImplementation&gt; 转换为SomeClass&lt;MyEnum, IMyInterface&gt;

我该如何解决这个问题?

例如,这不会编译:

var c = new MyContainer<MyEnum>();
c.MyProp = new SomeClass<MyEnum, MyImplementation>();

这是dot net fiddle

【问题讨论】:

  • 简单答案:使用public class SomeClass&lt;TEnum, out S&gt;。这很好,如果IMyInterface类型的成员在构造后不会改变。
  • @MarkShevchenko:据我了解,你不能用一个类来做到这一点,只有一个接口。
  • 我不确定,但代表可以是逆变的。让我来验证一下。
  • 你是对的。很抱歉草率和错误的答案。

标签: c# generics covariance


【解决方案1】:

您可以通过使泛型类型参数保持不变(协变或逆变,取决于其成员)来使其工作。但是,在 C# 中,您只能在接口上声明泛型参数不变,因此您必须声明另一个接口:

public interface ISomeClass<TEnum, in S>
{

}

public class SomeClass<TEnum, S> : ISomeClass<TEnum, IMyInterface>
    where TEnum : struct, IConvertible
    where S : IMyInterface
{

}

public class MyContainer<TEnum> where TEnum : struct, IConvertible
{
    public ISomeClass<TEnum, IMyInterface> MyProp { get; set; }
}

这将使以下代码编译:

var container = new MyContainer<DayOfWeek>();
container.MyProp = new SomeClass<DayOfWeek, MyImplementation>();

另一种可能的解决方案是使用另一个接口,其中不存在S 泛型类型参数:

public interface ISomeClass<TEnum>
    where TEnum: struct, IConvertible
{

}

public class SomeClass<TEnum, S> : ISomeClass<TEnum>
    where TEnum : struct, IConvertible
    where S : IMyInterface
{

}

public class MyContainer<TEnum> where TEnum : struct, IConvertible
{
    public ISomeClass<TEnum> MyProp { get; set; }
}

奖金 - 至于为什么它不起作用:

假设您的代码可以编译,只要T 实现IT,您就可以将MyClass&lt;T&gt; 分配给MyClass&lt;IT&gt;。你可以有以下课程:

class MyClass<T>
{
    public List<T> MyProp { get; set; }
}

然后做

MyClass<IMyInterface> instance = new MyClass<MyInterfaceImplementation>();

instance.MyProp 将是 List&lt;MyInterfaceImplementation&gt;,但您可以像访问 List&lt;IMyInterface&gt; 一样访问它,因此您可以尝试添加 MyOtherInterfaceImplementation 的元素,这会在运行时崩溃。不好玩。

【讨论】:

  • 你知道为什么只有接口和委托可以有变体类型参数吗?
  • 我没有这个问题的答案。
  • 如果有人对原因感兴趣:stackoverflow.com/questions/2733346/…
  • 感谢您的回答,但这让我遇到了无法向界面添加任何属性的问题。如果我添加一个属性,那么编译器会抱怨“类型参数'S'必须在...上始终有效”
  • 使用out而不是in更改方差类型。它将允许您添加 get-only 属性以及返回 S 的方法,但您将无法拥有采用 S 类型参数的 set-properties 和方法。
猜你喜欢
  • 2022-01-21
  • 2023-03-22
  • 1970-01-01
  • 2016-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-11
  • 2019-09-16
相关资源
最近更新 更多