読者です 読者をやめる 読者になる 読者になる

1.21 jigowatts

Great Scott!

C# yield returnでデータを作成する

C#

概要

今月のSoftware Designの特集「今すぐ実践できる良いプログラムの書き方」C#編の記事中にyield returnについての話がありました。
このyield return見たことはあるけど使ったことないなということで、ちょっと触ってみます。

Productクラスのコレクションを一定の規則に従い生成する

イマイチ使いどころがわからなかったyield return、どうやらデータを作成するお仕事が得意のようです。

snippet
public static IEnumerable<Product> ProductFactory(int count)
{
    for (int i = 0; i < count; i++)
    {
        var num = i + 1;
        yield return new Product()
        {
            ID = num,
            Name = string.Format("Prod{0:D9}", num),
            Category = string.Format("C-{0:D9}", num),
            Lot = 2 * num,
            Amount = 3 * num
        };
    }
}

利用するタイミングでyield returnによりProductクラスが生成されるため、一度に大量のコレクションを生成するよりメモリの効率がいいとのこと。

private static void Hoge()
{
    var count = 3;
    var products = ProductFactory(count);

    foreach (var product in products)
    {
        var props = product.GetType().GetProperties();
        foreach (var prop in props)
        {
            var name = prop.Name;
            var value = prop.GetValue(product, null) == null
                ? string.Empty
                : prop.GetValue(product, null).ToString();
            Console.Write("{0}: {1} ", name, value);
        }
        Console.WriteLine("");
    }
}

出力

ID: 1 Name: Prod000000001 Category: C-000000001 Lot: 2 Amount: 3
ID: 2 Name: Prod000000002 Category: C-000000002 Lot: 4 Amount: 6
ID: 3 Name: Prod000000003 Category: C-000000003 Lot: 6 Amount: 9

検証

ということで、従来のリストに詰める方法と比べてみました。

public static IEnumerable<Product> ProductFactoryAtonce(int count)
{
    var products = new List<Product>();

    for (int i = 0; i < count; i++)
    {
        var num = i + 1;
        var product = new Product()
        {
            ID = num,
            Name = string.Format("Prod{0:D9}", num),
            Category = string.Format("C-{0:D9}", num),
            Lot = 2 * num,
            Amount = 3 * num
        };
        products.Add(product);
    }
    return products;
}

結果としては、私のPCでは100万件程度であれば目立った差はなし。
1000万件で一度にリスト生成する方はメモリ使用率が上がり、1億件では数分待ってもリストができなかったのであきらめました。

f:id:sh_yoshida:20160325170530p:plain

一方yield returnでの実装ではメモリ使用率に大きな変化はなく、コンソール出力は滝のようにながれておりました。
なるほど、わからん!大量データを作成するときにメモリ効率がいいのはわかったけど、こんなコード書く機会なかったしなぁ。もっと実用的な使い方があるはず…。ちょっとMSDN読んでくる(;`・ω・)