如何在C#中创建带验证的不可变CAN总线帧值类型?
C# CAN帧不可变类型实现方案
核心需求回顾
需要定义一个满足以下要求的CAN总线帧类型:
- 不可变性:实例创建后无法修改内部状态
- 值比较:判断相等时按字段值(包括数组内容)而非引用比较
- 数据合法性:
Data属性长度不超过8字节(CAN协议限制)
最终推荐实现(引用类型Record)
using System.Collections; record CanFrame { private readonly int _id; private readonly byte[] _data = Array.Empty<byte>(); public CanFrame(int id, byte[] data) { _id = id; _data = ValidateAndCopyData(data); } public int Id { get => _id; init => _id = value; } public byte[] Data { get => (byte[])_data.Clone(); init => _data = ValidateAndCopyData(value); } private static byte[] ValidateAndCopyData(byte[] data) { ArgumentNullException.ThrowIfNull(data); if (data.Length > 8) { throw new ArgumentOutOfRangeException( nameof(data), "CAN帧数据长度不能超过8字节"); } return (byte[])data.Clone(); } protected override bool PrintMembers(StringBuilder builder) { builder.Append($"Id = {_id}, Data = [{string.Join(", ", _data)}]"); return true; } public override bool Equals(object? obj) { return obj is CanFrame other && _id == other._id && StructuralComparisons.StructuralEqualityComparer.Equals(_data, other._data); } public override int GetHashCode() { return HashCode.Combine( _id, StructuralComparisons.StructuralEqualityComparer.GetHashCode(_data)); } }
关键细节说明
不可变性保障
- 私有
readonly字段存储核心数据,防止直接修改 Data属性的getter返回数组副本,外部修改返回值不会影响实例内部状态- 统一用
ValidateAndCopyData方法处理验证与复制,消除构造函数和init setter的代码重复
- 私有
合法性验证
- 强制验证
Data非空且长度≤8,无论是通过构造函数还是with表达式创建实例,都会触发验证 - 使用
ArgumentNullException.ThrowIfNull简化空值检查代码
- 强制验证
值比较正确性
- 重写
Equals和GetHashCode,用StructuralComparisons.StructuralEqualityComparer实现数组内容的深度比较,而非默认的引用比较
- 重写
CS8618警告解决
- 给
_data字段初始化空数组Array.Empty<byte>(),让编译器明确字段永远非空,消除警告
- 给
readonly record struct实现(值类型可选方案)
如果需要值类型实现,需处理无参构造默认实例的问题:
using System.Collections; readonly record struct CanFrame { private readonly int _id; private readonly byte[] _data = Array.Empty<byte>(); public CanFrame(int id, byte[] data) { _id = id; _data = ValidateAndCopyData(data); } public int Id { get => _id; init => _id = value; } public byte[] Data { get => (byte[])_data.Clone(); init => _data = ValidateAndCopyData(value); } private static byte[] ValidateAndCopyData(byte[] data) { ArgumentNullException.ThrowIfNull(data); if (data.Length > 8) { throw new ArgumentOutOfRangeException( nameof(data), "CAN帧数据长度不能超过8字节"); } return (byte[])data.Clone(); } public bool Equals(CanFrame other) { return _id == other._id && StructuralComparisons.StructuralEqualityComparer.Equals(_data, other._data); } public override int GetHashCode() { return HashCode.Combine( _id, StructuralComparisons.StructuralEqualityComparer.GetHashCode(_data)); } }
注:default(CanFrame)会创建Id=0、Data为空数组的合法空帧,符合CAN协议规范。
内容的提问来源于stack exchange,提问作者sloopy
相关产品推荐
相关产品推荐

