【发布时间】:2019-07-03 15:45:57
【问题描述】:
我有一个级联的三个抽象泛型类,如下:
public abstract class SpawnTrigger<T>
public abstract class SpawnerConfig <T, S> where T : SpawnTrigger<S> {}
public abstract class Spawner<T, S> where T:SpawnerConfig<SpawnTrigger<S>, S>{}
我有三个对应的具体类。每一个都继承自其中一个抽象泛型类,如下所示:
public class OrbSpawnTrigger : SpawnTrigger<Orb>{}
public class OrbSpawnerConfig : SpawnerConfig<OrbSpawnTrigger, Orb>{}
public class OrbSpawner : Spawner<OrbSpawnerConfig, Orb>{}
为简单起见,我删除了上面这些类的主体。
编译器抱怨OrbSpawner。具体来说,它抱怨:
The type 'OrbSpawnerConfig' must be convertible to 'SpawnerConfig<SpawnTrigger<Orb>,Orb>' in order to use it as parameter 'T' in the generic class 'Spawner<T,S>'
我不明白它为什么抱怨。 OrbSpawnerConfig 是SpawnerConfig<SpawnTrigger<Orb>, Orb>:
OrbSpawnerConfig -> SpawnerConfig<OrbSpawnTrigger, Orb> -> SpawnerConfig<SpawnTrigger<Orb>, Orb>
有人理解这个错误吗?我正在使用 C# 开发 Unity 项目。是否存在我不知道的 C# 的特殊性或局限性?
我应该提到以下内容不会产生错误 - 但这不是解决方案,因为我想指定一个具体类而不是继承的通用抽象:
public class OrbSpawner : Spawner<SpawnerConfig<SpawnTrigger<Orb>, Orb>, Orb>
使用以下代码很容易重现错误:
namespace Test
{
public abstract class SpawnTrigger<T> {}
public abstract class SpawnerConfig<T, S> where T : SpawnTrigger<S> {}
public abstract class Spawner<T, S> where T : SpawnerConfig<SpawnTrigger<S>, S> {}
public class OrbSpawnTrigger : SpawnTrigger<Orb> { }
public class OrbSpawnerConfig : SpawnerConfig<OrbSpawnTrigger, Orb> { }
public class OrbSpawner : Spawner<OrbSpawnerConfig, Orb> {}
}
这是重构为使用简单类名的相同代码(错误在 ConcreteC 的定义中 - ConcreteB 不能用作参数 V)
namespace Test
{
public abstract class A<T> {}
public abstract class B<T, U> where U : A<T> {}
public abstract class C<T, V> where V : B<T, A<T>> {}
public class ConcreteA : A<MyObj> {}
public class ConcreteB : B<MyObj, ConcreteA> {}
public class ConcreteC : C<MyObj, ConcreteB> {}
public class MyObj {}
}
感谢 Dmitry Dovgopoly 提供有效的代码解决方案,感谢 PetSerAI 解释我的代码出现问题的原因。据我了解,我不能使用派生的 ConcreteB 来满足参数 V 的更一般要求的约束。解决方案是使用带 out 关键字的接口来创建协变接口。见covariant generic interface
【问题讨论】:
-
Variance 不适用于类。这意味着
SpawnerConfig<A, Orb>->SpawnerConfig<B, Orb>仅当A等于B时为真,但当A继承自B时不成立。