LoginSignup
0
1

More than 1 year has passed since last update.

ref readonly(inパラメーター修飾子)からReadOnlySpanを構築する

Last updated at Posted at 2019-11-27

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の許可が必要になります。

追記: 参照先がスタック上に存在しないと不正なポインターとなりクラッシュする可能性が有ります。
配列やclassに含まれる値型の参照からReadOnlySpanを構築してはいけません。

netstandard2.0
        public static ReadOnlySpan<T> CreateReadOnlySpan<T>(in T reference, int length)
            where T : unmanaged
        {
            unsafe
            {
                return new ReadOnlySpan<T>(Unsafe.AsPointer(ref Unsafe.AsRef(in reference)), length);
            }
        }

.NET Standard 2.1 向け

netstandard2.1
        public static ReadOnlySpan<T> CreateReadOnlySpan<T>(in T reference, int length)
            where T : unmanaged
        {
            return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in reference), length);
        }

他のユーティリティメソッド

オマケです。
Unsafeクラスのメソッドはほぼ通常の参照(ref T)しか引数に取らないため、読み取り専用参照を扱うメソッドを用意してみました。

    public static class ReadOnlyRefUnsafe
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static ReadOnlySpan<T> CreateReadOnlySpan<T>(in T reference, int length)
            where T : unmanaged
        {
#if BEFORE_NET_STANDARD21
            unsafe
            {
                return new ReadOnlySpan<T>(Unsafe.AsPointer(ref Unsafe.AsRef(in reference)), length);
            }
#else
            return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in reference), length);
#endif
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static ref readonly T Add<T>(in T source, int elementOffset)
            => ref Unsafe.Add(ref Unsafe.AsRef(in source), elementOffset);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static ref readonly T Add<T>(in T source, IntPtr elementOffset)
            => ref Unsafe.Add(ref Unsafe.AsRef(in source), elementOffset);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static ref readonly T AddByteOffset<T>(in T source, IntPtr byteOffset)
            => ref Unsafe.AddByteOffset(ref Unsafe.AsRef(in source), byteOffset);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool AreSame<T>(in T left, in T right)
            => Unsafe.AreSame(ref Unsafe.AsRef(in left), ref Unsafe.AsRef(in right));

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static ref readonly TTo As<TFrom, TTo>(in TFrom source)
            => ref Unsafe.As<TFrom, TTo>(ref Unsafe.AsRef(in source));

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static IntPtr ByteOffset<T>(in T origin, in T target)
            => Unsafe.ByteOffset(ref Unsafe.AsRef(in origin), ref Unsafe.AsRef(in target));

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool IsAddressGreaterThan<T>(in T left, in T right)
            => Unsafe.IsAddressGreaterThan(ref Unsafe.AsRef(in left), ref Unsafe.AsRef(in right));

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool IsAddressLessThan<T>(in T left, in T right)
            => Unsafe.IsAddressLessThan(ref Unsafe.AsRef(in left), ref Unsafe.AsRef(in right));

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static T Read<T>(in byte source)
            => As<byte, T>(in source);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static ref readonly T Subtract<T>(in T source, int elementOffset)
            => ref Unsafe.Subtract(ref Unsafe.AsRef(in source), elementOffset);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static ref readonly T Subtract<T>(in T source, IntPtr elementOffset)
            => ref Unsafe.Subtract(ref Unsafe.AsRef(in source), elementOffset);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static ref readonly T SubtractByteOffset<T>(in T source, IntPtr byteOffset)
            => ref Unsafe.SubtractByteOffset(ref Unsafe.AsRef(in source), byteOffset);
    }
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1