C# 7.2から追加された読み取り専用参照(ref readonly T
)ですが、残念ながらそのままReadOnlySpan<T>
を構築出来ません。
読み取り専用ではない通常の参照(ref T
)であれば、Span<T>
をMemoryMarshal.CreateSpan
で構築出来ますが、ReadOnlySpan<T>
を構築するMemoryMarshal.CreateReadOnlySpan
の引数はref T
となっているため、読み取り専用参照(ref readonly T
)からは構築出来ません。
また、.NET Standard 2.0ではMemoryMarshal.CreateReadOnlySpan
がありません。
ReadOnlySpanを構築する方法
読み取り専用参照から通常の参照を得るため、System.Runtime.CompilerServices.Unsafe.AsRef(in reference)
を使用します。
NugetでSystem.Runtime.CompilerServicesを追加する必要があります。
.NET Standard 2.0 向け
MemoryMarshal.CreateReadOnlySpan
が無いため、ポインターからReadOnlySpan<T>
を構築します。
Unsafeの許可が必要になります。
netstandard2.0
publicstaticReadOnlySpan<T>CreateReadOnlySpan<T>(inTreference,intlength)whereT:unmanaged{unsafe{returnnewReadOnlySpan<T>(Unsafe.AsPointer(refUnsafe.AsRef(inreference)),length);}}
.NET Standard 2.1 向け
netstandard2.1
publicstaticReadOnlySpan<T>CreateReadOnlySpan<T>(inTreference,intlength)whereT:unmanaged{returnMemoryMarshal.CreateReadOnlySpan(refUnsafe.AsRef(inreference),length);}
他のユーティリティメソッド
オマケです。
Unsafeクラスのメソッドはほぼ通常の参照(ref T
)しか引数に取らないため、読み取り専用参照を扱うメソッドを用意してみました。
publicstaticclassReadOnlyRefUnsafe{[MethodImpl(MethodImplOptions.AggressiveInlining)]publicstaticReadOnlySpan<T>CreateReadOnlySpan<T>(inTreference,intlength)whereT:unmanaged{#if BEFORE_NET_STANDARD21
unsafe{returnnewReadOnlySpan<T>(Unsafe.AsPointer(refUnsafe.AsRef(inreference)),length);}#else
returnMemoryMarshal.CreateReadOnlySpan(refUnsafe.AsRef(inreference),length);#endif
}[MethodImpl(MethodImplOptions.AggressiveInlining)]publicstaticrefreadonlyTAdd<T>(inTsource,intelementOffset)=>refUnsafe.Add(refUnsafe.AsRef(insource),elementOffset);[MethodImpl(MethodImplOptions.AggressiveInlining)]publicstaticrefreadonlyTAdd<T>(inTsource,IntPtrelementOffset)=>refUnsafe.Add(refUnsafe.AsRef(insource),elementOffset);[MethodImpl(MethodImplOptions.AggressiveInlining)]publicstaticrefreadonlyTAddByteOffset<T>(inTsource,IntPtrbyteOffset)=>refUnsafe.AddByteOffset(refUnsafe.AsRef(insource),byteOffset);[MethodImpl(MethodImplOptions.AggressiveInlining)]publicstaticboolAreSame<T>(inTleft,inTright)=>Unsafe.AreSame(refUnsafe.AsRef(inleft),refUnsafe.AsRef(inright));[MethodImpl(MethodImplOptions.AggressiveInlining)]publicstaticrefreadonlyTToAs<TFrom,TTo>(inTFromsource)=>refUnsafe.As<TFrom,TTo>(refUnsafe.AsRef(insource));[MethodImpl(MethodImplOptions.AggressiveInlining)]publicstaticIntPtrByteOffset<T>(inTorigin,inTtarget)=>Unsafe.ByteOffset(refUnsafe.AsRef(inorigin),refUnsafe.AsRef(intarget));[MethodImpl(MethodImplOptions.AggressiveInlining)]publicstaticboolIsAddressGreaterThan<T>(inTleft,inTright)=>Unsafe.IsAddressGreaterThan(refUnsafe.AsRef(inleft),refUnsafe.AsRef(inright));[MethodImpl(MethodImplOptions.AggressiveInlining)]publicstaticboolIsAddressLessThan<T>(inTleft,inTright)=>Unsafe.IsAddressLessThan(refUnsafe.AsRef(inleft),refUnsafe.AsRef(inright));[MethodImpl(MethodImplOptions.AggressiveInlining)]publicstaticTRead<T>(inbytesource)=>As<byte,T>(insource);[MethodImpl(MethodImplOptions.AggressiveInlining)]publicstaticrefreadonlyTSubtract<T>(inTsource,intelementOffset)=>refUnsafe.Subtract(refUnsafe.AsRef(insource),elementOffset);[MethodImpl(MethodImplOptions.AggressiveInlining)]publicstaticrefreadonlyTSubtract<T>(inTsource,IntPtrelementOffset)=>refUnsafe.Subtract(refUnsafe.AsRef(insource),elementOffset);[MethodImpl(MethodImplOptions.AggressiveInlining)]publicstaticrefreadonlyTSubtractByteOffset<T>(inTsource,IntPtrbyteOffset)=>refUnsafe.SubtractByteOffset(refUnsafe.AsRef(insource),byteOffset);}