エンティティを匿名型で手軽に実装する (2)

前回のエンティティを匿名型で手軽に実装する (1) で作成した EntityType<TEntity> では、
インスタンスを初期化するために毎回 ConstructorInfo を経由していましたが、
少し重い処理のため、回数が多いと処理時間に影響します。

そこで、あらかじめコンストラクターをラムダ式の式ツリーからコンパイルしておくという方法があります。
先にコードを示します。

取得した ConstructorInfo をもとに、

p => new TEntity((int)p[0], (string)p[1])

のような処理に相当する式ツリーを構築してコンパイルすることで、object[] から TEntity を初期化する関数が得られます。

CreateEntity メソッドの呼び出しを 100 万回実行して計測してみると、手元の環境だと前回のコードでは

  • EntityType の初期化: 0.0006 秒
  • CreateEntity メソッド 100 万回: 0.6 秒

だったのが、今回のコードでは

  • EntityType の初期化: 0.006 秒
  • CreateEntity メソッド 100 万回: 0.06 秒

になりました。

 

前回: エンティティを匿名型で手軽に実装する (1)

作成したサンプル
ExpressionsConsole (GitHub)

参照
Expression<TDelegate> クラス

エンティティを匿名型で手軽に実装する (1)

ここでは、次のようなものをエンティティ型と呼ぶことにします。

  • プロパティが定義されている
  • 各プロパティの値を指定してインスタンスを初期化する方法 (コンストラクターなど) が用意されている

通常、これを C# で実装すると次の図のようになり、少し手間がかかると思うことがあります。
Class1 はコンストラクターの実装が必要であり、Class2 はインスタンス生成時にプロパティ名を指定する必要があります。

エンティティ型の宣言

 

そこで今回は、匿名型を活用してこのようなエンティティ型を定義する方法を考えてみたいと思います。

実は匿名型は、プロパティは読取り専用で定義され、プロパティの数だけ引数を持つコンストラクターが 1 つだけ定義されます。
つまり、ちょうど上記の Class1 のような構造をしています。

このコンストラクターにアクセスできれば、任意のタイミングで匿名型のインスタンスを生成できるというわけです。
そこで、次のように EntityType クラスを実装してみました。

匿名型によりプロパティを宣言し、CreateEntity メソッドによりインスタンスを初期化できます。
CreateEntity メソッドの引数は params object[] のため IntelliSense は利きませんが、簡単に書けている気がします。
ただし匿名型のため、ローカル スコープを抜けられないという制限があります。

 

次に、これを CSV ファイルの読み込みに応用します。
CsvFile クラスで、読み込んだデータを指定された型に変換します。

(なお、ConvertHelper.cs は省略されています。完全なソースコードは ExpressionsConsole – GitHub にあります。)

このようにすると、CsvFile クラスを呼び出す側では IntelliSense が利き、LINQ to Objects でアクセスできます。

CsvFile (IntelliSense)

 

さて、インスタンスを生成するときに毎回 ConstructorInfo にアクセスするのは少しコストがかかるので、
次回は式ツリーを利用してパフォーマンスを改善したいと思います。

次回: エンティティを匿名型で手軽に実装する (2)

作成したサンプル
ExpressionsConsole (GitHub)

参照
匿名型 (C# プログラミング ガイド)