はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【C#】RuntimeHelpers.IsReferenceOrContainsReferences<T>メソッドを利用して"参照型"か"参照型を含む値型"か調べる

はじめに

今回はRuntimeHelpers.IsReferenceOrContainsReferences<T>について紹介をしたいと思います。

learn.microsoft.com

概要

public static bool IsReferenceOrContainsReferences<T> ();

指定された型が参照型であるか、または参照を含む値型であるかを示す値を返します。

learn.microsoft.com

使い方

public static void Main(string[] args)
{
    // True
    Console.WriteLine(RuntimeHelpers.IsReferenceOrContainsReferences<SampleClass>());
        
    // True
    Console.WriteLine(RuntimeHelpers.IsReferenceOrContainsReferences<ContainsReferenceStruct>());

    // False
    Console.WriteLine(RuntimeHelpers.IsReferenceOrContainsReferences<NoContainsReferencesStruct>());
}

public class SampleClass
{
    public int Value;
}

public struct ContainsReferenceStruct
{
    public int Value;
    public string Str;
}

public struct NoContainsReferencesStruct
{
    public int Value;
}

考察

RuntimeHelpers.IsReferenceOrContainsReferencesfalseの場合は、データがメモリの上に一列に並ぶことが保証されています。

public static void Main(string[] args)
{
    var sample = new Sample()
    {
        X = 10,
        Y = 2,
        Z = 999,
    };
        
    // a, 0, 0, 0
    Console.WriteLine(string.Join(",", BitConverter.GetBytes(10).Select(x => x.ToString("x"))));
        
    // 2, 0, 0, 0
    Console.WriteLine(string.Join(",", BitConverter.GetBytes(2).Select(x => x.ToString("x"))));
        
    // e3, 3, 0, 0
    Console.WriteLine(string.Join(",", BitConverter.GetBytes(999).Select(x => x.ToString("x"))));
        
    if (!RuntimeHelpers.IsReferenceOrContainsReferences<Sample>())
    {
        // sampleが利用しているバイト数だけメモリを確保する
        // new byte[]と同じくマネージドメモリを確保するが、配列の要素はゼロ初期化されていないのでちょっと早い
        var array = GC.AllocateUninitializedArray<byte>(Unsafe.SizeOf<Sample>());
            
        // arrayのメモリ領域にsampleを書き込む
        Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(array.AsSpan()), sample);
            
        // 中身がちゃんと書き込まれているかチェックする
        // a,0,0,0,2,0,0,0,e7,3,0,0
        Console.WriteLine(string.Join(",", array.Select(x => x.ToString("x"))));
    }
}

public struct Sample
{
    public int X;
    public int Y;
    public int Z;
}