C#类型擦除问题

nbe*_*e11 0 c# generics

将状态机放在C#中.我有一个接口和抽象实现类:

public interface IState<SD> where SD : IStateData {
    T EnterState<T>(InputData inputData, SD movementData) where T : IState<SD>;
}

public abstract class AMovementState : IState<MovementData> {
    public abstract AMovementState EnterState<AMovementState>(InputData inputData, MovementData movementData);
}
Run Code Online (Sandbox Code Playgroud)

MovementData实现IStateData所以这似乎应该工作正常.但是我收到了编译错误:

错误CS0425:类型参数AMovementState' of methodAMovementState.EnterState(InputData,MovementData)'的约束必须与类型参数IState.EnterState(InputData,MovementData)'的约束匹配T' of interface method.考虑使用显式接口实现(CS0425)(Assembly-CSharp)

我认为这是类型擦除的问题,但我不是100%.我需要修改我的方法还是我错过了一些愚蠢的东西?来自大多数Java背景.

Eri*_*ert 5

我认为这是类型擦除的问题

C#没有类型擦除.C#具有深深烘焙到运行时的实际泛型类型.

首先,你犯了一个常见的错误; 你这样做了:

class C {
  C M<C>() { ... }
Run Code Online (Sandbox Code Playgroud)

哦痛苦.这很难读.方法中的C是方法声明的C,而不是类声明的C. 你做了同样的事情:

public abstract class AMovementState : IState<MovementData> {
  public abstract AMovementState EnterState<AMovementState>(
    InputData inputData, MovementData movementData);
}
Run Code Online (Sandbox Code Playgroud)

这里也是一样的.你已经声明了两个东西,包括AMovementState一个类类型和一个类型参数.请不要这样做.这对代码的读者来说是非常误导的.

其次,一旦解决了这个问题:C#要求实现接口方法的泛型方法完全匹配该接口方法的约束.

现在,您可能会注意到,在这种特殊情况下,没有逻辑要求实现方法具有约束.类型系统可以设计成在这种情况下,通过类调用给出一个较小的约束,并通过接口调用给出一个更大的约束,这将是类型安全的.

但C#不是那样设计的; 它的设计使得需要约束来匹配,即使在严格说来不必要的情况下也是如此.

所以,你有两个选择:

  • 将约束添加到抽象方法,或
  • 执行错误消息所说的内容.创建一个调用类方法的显式接口方法.

那是:

public abstract class AMovementState : IState<MovementData> {
    public abstract M EnterState<M>
    (InputData inputData, MovementData movementData)
    where M : IState<MovementData>;
}
Run Code Online (Sandbox Code Playgroud)

要么

public abstract class AMovementState : IState<MovementData> {
    public abstract M EnterState<M>
    (InputData inputData, MovementData movementData);

    M IState<MovementData>.EnterState<M> 
    (InputData inputData, MovementData movementData)
    {
      this.EnterState<M>(inputData, movementData);
    }
}
Run Code Online (Sandbox Code Playgroud)

具有讽刺意味的是,C#不允许在显式接口方法上重复约束; 它会自动推断出来.我一直认为这是C#最奇怪的功能之一.

更一般地说:你可能患有通用性幸福病,即使它不必要且难以理解,也希望将事物变得通用.问问自己一件事物是否真的需要通用.泛型可能很难推理; 也许你可以使用更简单的抽象.