TTYF ~earlgrey の雑記~

主に自分用メモとか

ExpressionTree の PropertyOrField

ExpressionTree でプロパティやフィールドの値を取得するとき、Expression.PropertyExpression.Field を使えば良いんですが、Expression.PropertyOrField なんてのもあり、こっちだとその名の通りプロパティでもフィールドでもどっちでも OK みたいです。
じゃあプロパティかフィールドかは気にせず常に PropertyOrField 使えば楽チンじゃん、っと思いたいところですが、なんかのオーバーヘッドがあって Property や Field 使うよりちょっとだけ遅いんじゃ?という気もします。というわけで調べてみましょ。

こんなコードを書いて、それぞれの式がどんな風にコンパイルされるかを見てみました。

class Program
{
    static void Main(string[] args)
    {
        var foo = Expression.Constant(new Foo { Property1 = 1, Field1 = 2});

        // Property でプロパティにアクセス
        var prop1 = Expression.Property(foo, "Property1");

        // PropertyOrField でプロパティにアクセス
        var prop2 = Expression.PropertyOrField(foo, "Property1");

        // Field でフィールドにアクセス
        var fld1 = Expression.Field(foo, "Field1");

        // PropertyOrField でフィールドにアクセス
        var fld2 = Expression.PropertyOrField(foo, "Field1");

        Console.ReadKey();
    }
}

class Foo
{
    public int Property1 { get; set; }
    public int Field1;
}

式ツリーのデバッグによると、DebugView プロパティで ExressionTree の中身が見られるらしいですね。というわけで早速見てみましょう。

f:id:s_earlgrey:20160214185632p:plain

あの、見えないんですけど...。なんでなんでなんで???

どうやら DebugView を使用するためには、.Net Framework のバージョンを 4 以上にしないといけないらしいです。SIer に勤めてると .Net 3.5 縛りの案件がまだまだ多くて、そのノリを引きずってしまっていました。
というわけで、気を取り直して現在の最新である .Net 4.6.1 に設定して再チャレンジ。

f:id:s_earlgrey:20160214184309p:plain

今度は OK ですね。この調子で他の式も見てみましょう。結果は次の通り。

prop1(Property)
.Constant(PropertyOrField.Foo).Property1
prop2(PropertyOrField)
.Constant(PropertyOrField.Foo).Property1
fld1(Field)
.Constant(PropertyOrField.Foo).Field1
fld2(PropertyOrField)
.Constant(PropertyOrField.Foo).Field1

Property/Field を使い分けた場合も PropertyOrField の場合も作られる式に特に違いはないみたいですね。
もしかすると式自体を構築するときにごくわずかな性能差があるかもしれないですが、コンパイルした式が場合によっては何百万回とか呼ばれるのに対し、式の構築は最初の 1 回だけなので気にするレベルじゃないですね。
というわけで、積極的に PropertyOrField を使って OK ですね。

2016/2/21 追記

パフォーマンスとは別の問題で PropertyOrField が適さないケースがあったので追記します。
大文字の使用規則ガイドラインに反するので普通はやらないし、VB だとそもそもできなかったりしますが、型のメンバに例えば Foo と foo のように大文字と小文字だけが異なるメンバが混在している場合、PropertyOrField でアクセスしようとすると AmbiguousMatchException が発生してしまいます。大文字小文字を区別させるオーバーロードは残念ながら存在しません。
そのような場合は Property(Expression, PropertyInfo) などを使う必要があります。