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);
}