12
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【C#】.NET9 では `void` インスタンスを作れなくなっていた話

Posted at

はじめに

参考:https://neue.cc/2011/12/13_358.html

.NET Framework 時代は ↓ で void のインスタンスを得られますが、.NET9 ではエラーになるようです。

using System.Runtime.Serialization;

var v = FormatterServices.GetUninitializedObject(typeof(void));// エラー!
Assert.Equal(typeof(void), v.GetType());

FormatterServices は古いクラスにマークされていたり .NET Framework -> .NET の流れで変更があったのでしょうか。コンパイルはエラーにならず、実行時エラーになるためランタイム側であれこれした結果かもしれません。

やはり void インスタンスを取得できるのはまずかったか・・・

サンプルコード

テストコード
using Xunit;

public class __VoidInstanceTest
{
    [Fact]
    void CreateVoidError()
    {
        // 少なくとも .net9 では例外になる
        Assert.Throws<ArgumentException>(() =>
            System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject(typeof(void)));
    }

    [Fact]
    void CreateRefStructError()
    {
        Assert.Throws<NotSupportedException>(() =>
            System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject(typeof(Span<int>)));
    }

    [Fact]
    void SkipConstructor()
    {
        var person = (Person?)System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject(typeof(Person));

        Assert.NotNull(person);
        Assert.Equal(null!, person.Name);
        Assert.Equal(0, person.Age);
    }
}

file class Person
{
    internal string Name { get; } = "Default Name";
    internal int Age { get; } = 20;

    public Person() { }

    public Person(string name, int age)
    {
        this.Name = name;
        this.Age = age;
    }
}

System.Runtime.CompilerServices.RuntimeHelpers

古いクラスマークされたクラスの置き換えクラスが用意されているようです。

そもそもの古いクラスはオブジェクトのシリアル化関係の用途のようです。GetUninitializedObject()コンストラクタを呼ばずにインスタンスを生成するという結構ハック的な処理をしています。

file class Person
{
    internal string Name { get; } = "Default Name";
    internal int Age { get; } = 20;

    public Person() { }

    public Person(string name, int age)
    {
        this.Name = name;
        this.Age = age;
    }
}

var person = (Person?)System.Runtime.CompilerServices.
    RuntimeHelpers.GetUninitializedObject(typeof(Person));

Assert.NotNull(person);
Assert.Equal(null!, person.Name);
Assert.Equal(0, person.Age);

↑ の Person クラスは引数ありと引数なしの2種類のコンストラクタがありますが、GetUninitializedObject() によって得られたインスタンスはどちらのコンストラクタも経由していません。全てのフィールドが 0 または null で埋められます。

おわりに

いろいろ試してみましたが、.NET9 で void のインスタンスを取得する方法は見つけられませんでした。一方で面白いクラス(RuntimeHelpers)を発見できたので、パフォーマンス関係に使えないか試してみようと思います。

12
5
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
12
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?