【问题标题】:Resolve compiler error in type constraint of generic type解决泛型类型约束中的编译器错误
【发布时间】: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>'

我不明白它为什么抱怨。 OrbSpawnerConfigSpawnerConfig&lt;SpawnTrigger&lt;Orb&gt;, Orb&gt;

OrbSpawnerConfig -> SpawnerConfig&lt;OrbSpawnTrigger, Orb&gt; -> SpawnerConfig&lt;SpawnTrigger&lt;Orb&gt;, Orb&gt;

有人理解这个错误吗?我正在使用 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&lt;A, Orb&gt; -> SpawnerConfig&lt;B, Orb&gt; 仅当A 等于B 时为真,但当A 继承自B 时不成立。

标签: c# unity3d generics


【解决方案1】:

如果您的参数可以是协变的,您将通过添加接口来解决此问题

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

Covariance and contravariance real world example

【讨论】:

  • 谢谢德米特里。我会研究你的答案,看看它是否对我来说是一个完整的解决方案。非常感谢 Covariance 文章的链接。
猜你喜欢
  • 2014-09-23
  • 2017-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多