c# - 解决泛型类型约束中的编译器错误
问题描述
我有三个抽象泛型类的级联,如下所示:
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 关键字的接口来创建协变接口。请参阅协变通用接口
解决方案
如果您的参数可以是协变的,您将通过添加接口来解决此问题
public interface ISpawnTrigger<out T>{}
public interface ISpawnerConfig<out T, S> where T : ISpawnTrigger<S>{}
public interface ISpawner<out T, S> where T : ISpawnerConfig<ISpawnTrigger<S>, S>{}
public abstract class SpawnTrigger<T> : ISpawnTrigger<T>{}
public abstract class SpawnerConfig<T, S> : ISpawnerConfig<T, S> where T : ISpawnTrigger<S>{}
public abstract class Spawner<T, S> : ISpawner<T, S> where T : ISpawnerConfig<ISpawnTrigger<S>, S>{}
public class OrbSpawnTrigger : SpawnTrigger<Orb>{}
public class OrbSpawnerConfig : SpawnerConfig<OrbSpawnTrigger, Orb>{}
public class OrbSpawner : Spawner<OrbSpawnerConfig, Orb>{}
public class Orb{}
推荐阅读
- macros - 正确编写具有命名符号的宏
- python - python中的咳嗽识别
- angular - 根据表单中的选择更改占位符
- python - TypeError:将一个函数传递给另一个函数时,“函数”对象不可下标
- stored-procedures - 如何将 VARCHAR2 的 TYPE VARRAY 参数列表传递给 PL-SQL 存储过程并在循环中访问参数列表
- python - 如何告诉python传递给函数的意外变量类型在上下文中是正确的
- sql - 如何通过 Oracle 中的 SQL 查询找到每个 id 的 TOP/MAX 值?
- reactjs - 为什么在 React 示例中使用 null 初始化 useRef?
- uwp - 如何在 UWP 应用程序中清除 MediaPlayer 的缓存?
- ios - swift 5 TextBox 值在标签中显示