이 글이 저에겐 실질적으로 도움이 됐습니다. 개념적으로 이해하는 것과 손이 나가는 것은 조금 다른 감각인 것 같네요. 마치 영어의 독해와 회화 같이요.
참고하여 Span 확장 클래스를 만들어 봤습니다. 같이 보시죠.
public static class SpanExtension
{
public static ReadOnlySpan<byte> AsReadOnlyBytes<T>(this ref T @this)
where T : struct
{
var span = MemoryMarshal.CreateReadOnlySpan(ref @this, 1);
return MemoryMarshal.Cast<T, byte>(span);
}
public static ReadOnlySpan<byte> AsReadOnlyBytes<T>(this T[] @this)
where T : struct
{
return MemoryMarshal.Cast<T, byte>(@this);
}
public static Span<byte> AsBytes<T>(this ref T @this)
where T : struct
{
var span = MemoryMarshal.CreateSpan(ref @this, 1);
return MemoryMarshal.Cast<T, byte>(span);
}
public static Span<byte> AsBytes<T>(this T[] @this)
where T : struct
{
return MemoryMarshal.Cast<T, byte>(@this);
}
}
확장을 보시면 MemoryMarshal의 기능을 이용한 것 뿐이지만, 강력합니다. 이 확장을 이용해 다음과 같이 Stream에 쓰거나 읽는 코드를 단순화 하였습니다.
public void Write(Stream s)
{
s.Write(체결시각.AsReadOnlyBytes());
s.Write(체결가.AsReadOnlyBytes());
s.Write(체결수량.AsReadOnlyBytes());
s.Write(매수호가잔량.AsReadOnlyBytes());
s.Write(매도호가잔량.AsReadOnlyBytes());
}
public static bool Read(Stream s, ref 실시간시세 v)
{
var length = s.Read(v.체결시각.AsBytes());
if (length == 0)
return false;
s.Read(v.체결가.AsBytes());
s.Read(v.체결수량.AsBytes());
v.매수호가잔량 = new decimal[5];
s.Read(v.매수호가잔량.AsBytes());
v.매도호가잔량 = new decimal[5];
s.Read(v.매도호가잔량.AsBytes());
return true;
}
AsReadOnlyBytes()
를 통해 메모리 할당 없이 해당 필드를 ReadOnlySpan로 변환하여 쓰기를 합니다.
더욱 재밌는 것은 AsBytes()
를 통해 해당 필드의 메모리 위치를 Span로 변환하여 바로 읽기할 수 있다는 것입니다.