BinaryWriter/Readerとエンディアン変換
.Net FrameworkのBinaryWriter/Readerはビッグエンディアンに対応していない。
ので、ビッグエンディアンを扱う場合はこんな感じで書いていた。
static void Main(string[] args) { using (var stream = new MemoryStream()) using (var writer = new BinaryWriter(stream)) { writer.Write(SwapBytes(0x1234)); } } static UInt16 SwapBytes(UInt16 value) { return (UInt16)((value << 8) | (value >> 8)); }
別にこれはこれでなんの不都合もないのだが、ふと暗黙の型変換を使うと綺麗にかけるのではないかと思い立つ。
public struct UInt16BigEndian { UInt16 value; public static implicit operator byte[](UInt16BigEndian target) { return new byte[] { (byte)(target.value >> 8), (byte)(target.value & 0xff) }; } public static implicit operator UInt16 (UInt16BigEndian target) { return target.value; } public static implicit operator UInt16BigEndian(UInt16 target) { return new UInt16BigEndian(target); } public static implicit operator UInt16BigEndian(byte[] bytes) { return new UInt16BigEndian((UInt16)((bytes[0] << 8) | bytes[1])); } public UInt16BigEndian(UInt16 value) { this.value = value; } }
こんな感じの型を定義する。
ついでにStreamの拡張メソッドも定義する。
static class StreamExtension { public static void WriteBytes(this Stream stream,byte[] bytes) { stream.Write(bytes, 0, bytes.Length); } public static byte[] ReadBytes(this Stream stream, int length) { var buf = new byte[length]; stream.Read(buf, 0, length); return buf; } }
すると、こんなふうに使える。
class Packet { public UInt16 Length { get; set; } public byte[] Data { get; set; } public byte[] ToByteArray() { using (var stream = new MemoryStream()) { stream.WriteBytes((UInt16BigEndian)Length); stream.WriteBytes(Data); return stream.ToArray(); } } public static Packet CreateFrom(byte[] bytes) { using (var stream = new MemoryStream(bytes)) { UInt16BigEndian length = stream.ReadBytes(2); byte[] data = stream.ReadBytes(length); return new Packet { Length = length, Data = data }; } } }
中々見た目が良い。