かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

Managed Extensibility Framework入門 その7.5「遅延初期化のLazy」

ずいぶん長いこと、MEFの記事をほったらかしていました。正直自分でも記事書いてたことを忘れてました。ということでリハビリも兼て、次の回のための予備知識であるLazyクラスについて説明してみようと思います。

遅延初期化

遅延初期化とは読んで字のごとく。初期化を遅延するということです。普通は、初期化といったらこんな感じですよね。

// Hogeというクラスがあると仮定してHogeクラスを初期化する
var hoge = new Hoge();

さて、上記のプログラムでHogeクラスの初期化を行いましたが、このhoge変数がいつ使われるのかというのは謎です。その直後に使われるのか?それとも10000行後で使われるのか?それとも特定のボタンが押されるまで出番はないのか?と色々なケースが考えられます。今回の例みたいにHogeクラス1つだったら大して気になりませんが。初期化するオブジェクト数が1000とかになってくると、1つのオブジェクトの初期化処理が10msくらいかかるとして10秒初期化だけでかかってしまいます。そんなとき、ぱっと考え付くのが、必要になったら作ればいいじゃん?という解決策です。


そんなプログラムを書くときに使われるテクニックで以下のようなプログラムを書くこともあったりします。

private Hoge hoge;
public Hoge Value
{
    get
    {
        if (hoge == null)
        {
            // 必要になった段階でインスタンスを作成する
            hoge = new Hoge();
        }
        return hoge;
    }
}

こんなプログラムを量産するのも疲れるってわけで、遅延初期化をサポートしてくれるクラスが.NET Frameworkには用意されています。Lazyという名前のクラスがそいつになります。ということで早速使ってみましょう。

static void Main(string[] args)
{
    {
        // この時点ではHogeクラスのインスタンスは作成されない
        var hoge = new Lazy<Hoge>();
        Console.WriteLine("Lazy<Hoge>を作成しました");
        // ここで初めてHogeのインスタンスが作成される
        Console.WriteLine(hoge.Value);
    }

    Console.WriteLine("---------------------------------");
            
    {
        // Hogeの初期化方法もデリゲートで指定可能
        var hoge = new Lazy<Hoge>(() => new Hoge(10));
        Console.WriteLine("Lazy<Hoge>を作成しました");
        // ここで初めてHogeのインスタンスが作成される
        Console.WriteLine(hoge.Value);
    }
}

使い方は簡単でLazy<遅延初期化したい型>のように書きます。コンストラクタでデリゲートを渡してデフォルトのコンストラクタ以外で初期化したり、初期化時に特定のメソッドを呼んだりとか色々できるようになってます。上記の実行結果を以下に示します。

Lazy<Hoge>を作成しました
Hogeが作成されました
LazySample.Hoge
---------------------------------
Lazy<Hoge>を作成しました
Hoge(10)が作成されました
LazySample.Hoge

Lazyのインスタンスを初期化したタイミングではなく、LazyのValueプロパティにアクセスしたタイミングで初めてインスタンスが作成されていることがわかります。


これがMEFにどうつながっていくのかというと、プラグインの多いプログラムを作る際に起動時に全部のプラグインを初期化してたら起動処理が凄い時間になってしまうという問題点や、使わないプラグインもメモリ上に乗ってしまうという問題がおきたりします。そういうときにLazyを使うと、プラグインが必要になるタイミングまでインスタンスが作られないという処理が簡単に出来るようになります。


ということで次回は、LazyをMEFで使ってみようと思います。

サンプルコードの入手

コチラからダウンロードできます。