0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[C#]コンストラクタを呼ばずにインスタンスを作る

Posted at

以下のようなクラスがあるとき、インスタンスの作成にとても時間がかかってしまいます。

public class Hoge
{
    private int v;
    public int Value => v;

    public Hoge()
    {
        // Application.isPlayingはメインスレッドで呼ばなければ例外が発生する
        Debug.Log($"Application.isPlaying:{Application.isPlaying}");
        // コンストラクタで重い処理をしている
        v = HeavyFunc();
    }
    
    private int HeavyFunc()
    {
        // 重い初期化処理
        Thread.Sleep(5000);
        // 計算結果
        return 42;
    }
}

インスタンスを作成するときにメインスレッドで行ってしまうとUIが止まってしまうのでできれば別スレッドで作成したいところです。
しかし、コンストラクタ内部でApplication.isPlayingにアクセスしてしまっているため通常はメインスレッドでしか作成することができません。

Task.Run(() =>
{
    try
    {
        var h = new Hoge();
        Debug.Log(h.Value);
    }
    catch (Exception e)
    {
        // UnityException: get_isPlaying can only be called from the main thread.
        // Constructors and field initializers will be executed from the loading thread when loading a scene.
        // Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.
        Debug.LogException(e);
    }
});

このクラスを書いたのが自分であれば修正すればいいですがソースコードを修正できない場合もあります。
そのような場合、最終手段として
System.Runtime.Serialization.FormatterServices.GetUninitializedObject
を使うことでコンストラクタを呼ばずにインスタンスを作成することができます。
インスタンスさえ作成できれば後はリフレクションで何とかなります。

// privateなフィールド、メソッドにアクセスするためにリフレクションを使用する
var vInfo = typeof(Hoge).GetField("v", BindingFlags.NonPublic | BindingFlags.Instance);
var heavyFuncInfo = typeof(Hoge).GetMethod("HeavyFunc", BindingFlags.NonPublic | BindingFlags.Instance);
_ = Task.Run(() =>
{
    try
    {
        // コンストラクタを呼ばずにインスタンスを作成
        var hoge = FormatterServices.GetUninitializedObject(typeof(Hoge)) as Hoge;
        // HeavyFuncを実行する
        var result = heavyFuncInfo.Invoke(hoge, null);
        // フィールドに値をセット
        vInfo.SetValue(hoge, result);
        // 正しくインスタンスが作成されている
        Debug.Log(hoge.Value);
    }
    catch (Exception e)
    {
        Debug.LogException(e);
    }
});
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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?