Int32 和 int 之间的泛型方法区别

Generic method difference between Int32 and int

我无法理解接口中的泛型方法。我正在使用带有自动 C# 版本的 .NET 框架 4.8,因此它应该是 C# 7.3。下面的例子

接口是:

public interface IRegister
{
  Nullable<T> Read<T>() where T: struct;
  void Write<T>(T value) where T : struct;
}

现在一些class继承自接口:

public abstract class AbstractRegister : IRegister
{
  protected ModbusClient client;
  protected int Address;
  public abstract Nullable<T> Read<T>() where T : struct;
  public virtual void Write<T>(T value) where T : struct
  {
    return;
  }
  public AbstractRegister(int address, ModbusClient client)
  {
    this.client = client;
    this.Address = address;
  }
}

现在我有一些 class 继承自 AbstractRegister:

class InputRegister_int : AbstractRegister
{
  public InputRegister_int(int address, ModbusClient client) : base(address, client) { }

  public override int? Read<int>()
  {
    if (client == null)
      return null;
    return this.client.ReadInputRegisters(this.Address, 1)[0] as int?;
  }
}

在行 public override int? Read<int>() 我有错误:

CS1001: Identifier expected
CS1003: Syntax error, '>' expected
CS1003: Syntax error, '(' expected

我正在使用 Visual Studio 社区 2022

当我使用 Int32 而不是 int 时,一切正常,它几乎解决了我的问题,但是 float 类型(这对我来说是必需的)没有 Float 表示。

我没有解决这个问题的想法了。有人可以给我解释一下吗?

也许尝试将接口更改为泛型,这样泛型类型引用就被限制为 相同 类型,而不是任意 any 类型:

public interface IRegister<T> 
    where T : struct
{
    Nullable<T> Read();
    void Write(T value);
}

public abstract class AbstractRegister<T> : IRegister<T>
    where T : struct
{
    protected ModbusClient client;
    protected int Address;
    public abstract Nullable<T> Read();
    public virtual void Write(T value)
    {
        return;
    }
    public AbstractRegister(int address, ModbusClient client)
    {
        this.client = client;
        this.Address = address;
    }
}

class InputRegister_int : AbstractRegister<int>
{
    public InputRegister_int(int address, ModbusClient client) : base(address, client) { }

    public override int? Read()
    {
        if (client == null)
          return null;
          return this.client.ReadInputRegisters(this.Address, 1)[0] as int?;
    }
}

class InputRegister_float : AbstractRegister<float>
{
    public InputRegister_float(int address, ModbusClient client) : base(address, client) { }

    public override float? Read()
    {
        if (client == null)
          return null;
          return this.client.ReadInputRegisters(this.Address, 1)[0] as float?;
    }
}

这里的功能区别在于,在 class 级别指定 通用类型 ,基础 class 中的所有 T 必须相同所以 InputRegister_int 所有的 T 现在都是 int.

从语法的角度来看,请注意这些方法的原型上没有后缀,这是因为它们根本不是泛型方法,它们被专门指定为相同的类型参数 创建实例时指定的。

如果你有这样的方法:

Nullable<T> Read<T>() where T: struct;

然后甚至 InputRegister_int class 我们可以通过 ANY 类型的结构传递给该方法并期望它 return值,以下内容预计在运行时有效:

var intRegister = new InputRegister_int();
int intValue = intRegister.Read<int>();
DateTime dtValue = intRegister.Read<DateTime>();

如果你真的希望你的寄存器是 any 任意类型,那么我们根本不需要 abstract 寄存器:

public class GenericRegister : IRegister
{
    protected ModbusClient client;
    protected int Address;
    public virtual Nullable<T> Read<T>() where T : struct
    {
        if (client == null)
            return null;
            return this.client.ReadInputRegisters(this.Address, 1)[0] as T?;        
    }
    public virtual void Write<T>(T value) where T : struct
    {
        // TODO: implement generic write logic
        return;
    }
    public AbstractRegister(int address, ModbusClient client)
    {
        this.client = client;
        this.Address = address;
    }
}

然而,这会导致非常懒惰的实现,您的 ModBus 寄存器中的值的类型不会改变,大多数固定在硬件实现中,但 PLC 中的内部逻辑会为您限制类型。您的固定类型 InputRegister_int 允许您在需要时对来自客户端的响应进行特定值和类型检查,这有助于指导开发人员稍后做出合理的决定。

这个答案专门针对“Int32”与问题的 int 部分。

Int32 在代码中用作“这个作品”的例子实际上并不是 System.Int32 (这正是 int 是什么)而只是 恰好匹配某种系统类型的名称。

下面的代码显示了更简单的示例 - 您可以看到“Int32”的行为与任何其他不是类型名称的字符串完全一样。

using System;
public class SomeClass {

    // exactly the same as T X<T>(), just using `Int32` as type parameter name
    Int32 X<Int32>() { return default(Int32); } 
    
    // fails to compile as `System.Int32` and `int` are concrete types
    System.Int32 Y<System.Int32>() { return default(System.Int32); } 
    int X<int>() { return default(int); } 
}

通常,泛型方法中类型参数的名称以 T 开头(只是 T 或以它为前缀 - TResult),但 C# 规范中有强制执行的规则.因此,任何不存在的类型名称都被视为“类型参数的名称”,只要它用在正确的位置 void M<AnythingThatIsNotType>(...) .