コンストラクタを回避してインスタンスを生成する(C#)

一時的にモックのようなものが欲しいことがありました。普通にnew(...)すればいいんですが、コンストラクタにたくさん引数があって準備が大変で嫌…みたいな感じです。

こういうときにRuntimeHelpers.GetUninitializedObject(Type)というのがあるみたいです。これを使うと、コンストラクタもフィールド初期化子も無視してインスタンスを生成できます。

interface IValue { int Value { get; } }
record Sample(int Value, string Name, DateTime Date) : IValue;

var sample = (Sample)RuntimeHelpers.GetUninitializedObject(typeof(Sample));
// sample.Date: DateTime.MinValue
if (sample is IValue v) { v.Value; }// 当然IValueとしても扱える

すごい。

昔はFormatterServices.GetUninitializedObjectが使われていたようですが、FormatterServicesはすでに古くなっています。

Formatter-based serialization is obsolete and should not be used. (https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.serialization.formatterservices)

このFormatterServices.GetUninitializedObjectですが昔はvoidのインスタンスも作れたみたいです。

neue cc - Voidインスタンスの作り方、或いはシリアライザとコンストラクタについて

ビックリですね。

現代の.NETではvoidのインスタンスは作れないようになっています。RuntimeHelpers.GetUninitializedObject(typeof(void))FormatterServicesも同様)すると例外になります。

System.ArgumentException : Type is not supported.

他に作れないものとして代表的なのはstringなどがあるようです。以下のあたりを見てみるとなんとな~くダメそうなものがわかります。

https://github.com/dotnet/runtime/blob/908c168c19935e61ceeb86ff90b0db611102fcc2/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs#L260-L309

可変長型やポインタ、ref構造体などもちゃんと?ダメなようです。

An unhandled error has occurred. Reload 🗙