Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/295.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 有没有办法防止/检测自动/隐式类型转换?_C#_.net - Fatal编程技术网

C# 有没有办法防止/检测自动/隐式类型转换?

C# 有没有办法防止/检测自动/隐式类型转换?,c#,.net,C#,.net,考虑这些方法,每个方法都从输入缓冲区提取字节: byte ReadByte(List<byte> data); //removes and returns 1 byte UInt16 ReadUInt16(List<byte> data); //removes and returns 2 bytes as a short UInt32 ReadUInt32(List<byte> data); //removes and returns 4 bytes as a

考虑这些方法,每个方法都从输入缓冲区提取字节:

byte ReadByte(List<byte> data); //removes and returns 1 byte
UInt16 ReadUInt16(List<byte> data); //removes and returns 2 bytes as a short
UInt32 ReadUInt32(List<byte> data); //removes and returns 4 bytes as an int
字节读取字节(列表数据)//删除并返回1字节
UInt16 ReadUInt16(列表数据)//删除并返回2个短字节
UInt32 ReadUInt32(列表数据)//删除并返回4个字节作为整数
现在我有了一个结构/类,如:

class Thing
{
 public byte a{get;set;}
 public UInt16 b{get;set;}
 public UInt32 c{get;set;}
 public byte d{get;set;}

 public void Read(List<byte> data)
 {
  a = ReadByte(data);
  b = ReadUInt16(data);
  c = ReadUInt16(data); //BUG this is a 32bit data
  d = ReadByte(data);
 }
}
类的东西
{
公共字节a{get;set;}
公共UInt16 b{get;set;}
公共UInt32 c{get;set;}
公共字节d{get;set;}
公共无效读取(列表数据)
{
a=读取字节(数据);
b=读数16(数据);
c=ReadUInt16(数据);//这是一个32位的数据
d=读取字节(数据);
}
}
由于
short
将自动升级为
int
,因此此代码编译和运行良好,但引入了难以发现的错误-它读取的
c
比它应该读取的少2个字节,并且所有后续读取的值都将是错误的

是否有任何技术可用于确保当
c
属于
UInt32
类型时,它不会接受
UInt16
或其他合法类型


理想情况下,
东西
不会被更改,但如果您的解决方案需要,也可以。

最好的方法是编写单元测试,验证输入是否与预期输出匹配。您还可以进行反射以自动执行逻辑或使用代码生成器执行操作


在构建时没有任何东西可以帮助您,除非您想编写Roslyn分析器。

这不是最优雅的事情,但是您可以将每个
Read*()
方法都设置为泛型,并执行看起来有点多余的类型检查。不幸的是,您不能将属性用作
out
ref
参数,否则这可能会简单得多:

UInt16 ReadUInt16<T>(List<byte> data, T _)
{
    if (typeof(T) != typeof(UInt16))
        // Throw exception
    
    // Perform normal process and return value.
}

答案是:你提供的方法是否定的

例如C#和.NET的CTS都是设计出来的,你不能避免这样的编码错误

但也就是说,您可以做的一件事是使用这样的通用方法:

static T Read<T>(this T instance, List<byte> data)
{
  switch ( instance )
  {
    case byte value:
      Console.WriteLine("Read byte");
      return default;
    case UInt16 value:
      Console.WriteLine("Read UInt16");
      return default;
    case UInt32 value:
      Console.WriteLine("Read Uint32");
      return default;
    default:
      string message = $"Type not supported for Read<T> (only byte or UInt16/32): "
                     + $"{typeof(T).Name}";
      throw new ArgumentException(message);
  }
}

class Thing
{
  public byte a { get; set; }
  public UInt16 b { get; set; }
  public UInt32 c { get; set; }
  public byte d { get; set; }
  public void Read(List<byte> data)
  {
    a = a.Read(data);
    b = b.Read(data);
    c = c.Read(data);
    d = d.Read(data);
  }
}

您可以创建包装类型,并将其用作方法以及属性的支持字段(随后需要显式处理)的返回类型,如:

class WrapperTypesPreventCast
{
    private MyByte _a;
    public byte a { get { return _a; } set { _a = value; } }
    private MyUInt16 _b;
    public UInt16 b { get { return _b; } set { _b = value; } }
    private MyUInt32 _c;
    public UInt32 c { get { return _c; } set { _c = value; } }

    public void Read(List<byte> data)
    {
        _a = ReadByte(data);
        _b = ReadUInt16(data);
        //_c = ReadUInt16(data);  // Would produce a compiler error.
        _c = ReadUInt32(data);    // Compiles
    }

    // Dummy implementations
    MyByte ReadByte(List<byte> data) => 0;
    MyUInt16 ReadUInt16(List<byte> data) => 0;
    MyUInt32 ReadUInt32(List<byte> data) => 0;
}

struct MyByte
{
    public byte Value { get; }

    public MyByte(byte value)
    {
        Value = value;
    }

    public static implicit operator byte(MyByte b) => b.Value;
    public static implicit operator MyByte(byte b) => new MyByte(b);
}

struct MyUInt16
{
    public UInt16 Value { get; }

    public MyUInt16(UInt16 value)
    {
        Value = value;
    }

    public static implicit operator UInt16(MyUInt16 b) => b.Value;
    public static implicit operator MyUInt16(UInt16 b) => new MyUInt16(b);
}

struct MyUInt32
{
    public UInt32 Value { get; }

    public MyUInt32(UInt32 value)
    {
        Value = value;
    }

    public static implicit operator UInt32(MyUInt32 b) => b.Value;
    public static implicit operator MyUInt32(UInt32 b) => new MyUInt32(b);
}
class WrapperTypesPreventCast
{
私有MyByte_a;
公共字节a{get{return}set{UE=value;}}
私有MyUInt16_b;
公共UInt16 b{get{return}set{{u b=value;}}
私有MyUInt32_c;
公共UInt32 c{get{return{UC;}集合{UC=value;}}
公共无效读取(列表数据)
{
_a=读取字节(数据);
_b=读数16(数据);
//_c=ReadUInt16(data);//将产生编译器错误。
_c=ReadUInt32(数据);//编译
}
//虚拟实现
MyByte ReadByte(列表数据)=>0;
MyUInt16 ReadUInt16(列表数据)=>0;
MyUInt32 ReadUInt32(列表数据)=>0;
}
结构MyByte
{
公共字节值{get;}
公共MyByte(字节值)
{
价值=价值;
}
公共静态隐式运算符字节(MyByte b)=>b.值;
公共静态隐式运算符MyByte(字节b)=>新的MyByte(b);
}
结构MyUInt16
{
公共UInt16值{get;}
公共货币单位16(货币单位16值)
{
价值=价值;
}
公共静态隐式运算符UInt16(MyUInt16 b)=>b.值;
公共静态隐式运算符MyUInt16(UInt16 b)=>新的MyUInt16(b);
}
结构MyUInt32
{
公共UInt32值{get;}
公共货币单位32(货币单位32值)
{
价值=价值;
}
公共静态隐式运算符UInt32(MyUInt32 b)=>b.值;
公共静态隐式运算符MyUInt32(UInt32 b)=>新的MyUInt32(b);
}
错误当然仍然可能发生,但由于冗余度较高,错误发生的可能性较小。此外,您现在可以轻松地为
Read*()
方法创建单元测试,并验证它们确实返回了正确的类型(这在原始代码中对您没有帮助,因为方法本身是正确的,只是用法不正确)


请注意,包装器类型中的隐式转换并不是严格必需的,但可以增加使用它们的舒适性。但是,您应该将可访问性限制在最低限度,否则这可能会导致代码中其他地方出现令人惊讶和混乱的转换。

什么类型的
a
b
c
d
?@PavelAnikhouski我认为它们与
a8
b16
等相同。@Mr.Boy不是。您的属性名称是
a8
等,因此它不是那么明显。您可以在
a
b
等的属性设置器中进行类型检查,并仅在类型匹配时进行设置。因此,您的问题是如何防止隐式类型转换。我不认为你能。投票人是否愿意分享你对这个答案的错误看法,这样我就可以改进它,或者在将来避免同样的错误?这是我一直想知道的想法,但没有完全弄清楚,谢谢。这很好,因为它允许你添加其他类型。我的一个疑问/担忧是,泛型仍然允许类型转换,还是它们允许类型转换?我以前也从未见过
开关
明确提到大小写类型,这很酷。这是C#7中引入的
开关
,但在此版本之前,您可以通过检查类型来使用
if…else…
。我不理解这个问题:这里我们不转换任何东西,因为当您调用
a=a.Read(data)
时,您知道运行时的类型。必须实现从数组到所需类型的字节转换和删除,因此,只有在您掌握了代码和问题方法的实现时,它才会起作用。或者您可以调用这些方法,因此泛型方法将是一个包装器。
var list = new List<byte> { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };

var instance = new Thing();

instance.Read(list);
class WrapperTypesPreventCast
{
    private MyByte _a;
    public byte a { get { return _a; } set { _a = value; } }
    private MyUInt16 _b;
    public UInt16 b { get { return _b; } set { _b = value; } }
    private MyUInt32 _c;
    public UInt32 c { get { return _c; } set { _c = value; } }

    public void Read(List<byte> data)
    {
        _a = ReadByte(data);
        _b = ReadUInt16(data);
        //_c = ReadUInt16(data);  // Would produce a compiler error.
        _c = ReadUInt32(data);    // Compiles
    }

    // Dummy implementations
    MyByte ReadByte(List<byte> data) => 0;
    MyUInt16 ReadUInt16(List<byte> data) => 0;
    MyUInt32 ReadUInt32(List<byte> data) => 0;
}

struct MyByte
{
    public byte Value { get; }

    public MyByte(byte value)
    {
        Value = value;
    }

    public static implicit operator byte(MyByte b) => b.Value;
    public static implicit operator MyByte(byte b) => new MyByte(b);
}

struct MyUInt16
{
    public UInt16 Value { get; }

    public MyUInt16(UInt16 value)
    {
        Value = value;
    }

    public static implicit operator UInt16(MyUInt16 b) => b.Value;
    public static implicit operator MyUInt16(UInt16 b) => new MyUInt16(b);
}

struct MyUInt32
{
    public UInt32 Value { get; }

    public MyUInt32(UInt32 value)
    {
        Value = value;
    }

    public static implicit operator UInt32(MyUInt32 b) => b.Value;
    public static implicit operator MyUInt32(UInt32 b) => new MyUInt32(b);
}