2016-07-20 4 views
1

次のように私はクラスを持っている:私が呼ぶことを好むだろうPropertyInfoからGetterバッキングフィールドを取得するには?

protected void OnAssignmentPropertyChanged<T>(string propertyName, Expression<Func<T>> fieldExpression, T value) 
    { 
     var get = fieldExpression.Compile(); 
     if (get().Equals(value)) 
     { 
      return; 
     } 

     // invoke set property method 
     SetProperty(fieldExpression, value); 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 

    private void SetProperty<T>(Expression<Func<T>> fieldExpression, T value) 
    { 
     if (fieldExpression == null) 
     { 
      throw new ArgumentNullException(nameof(fieldExpression)); 
     } 

     var memberExpression = fieldExpression.Body as MemberExpression; 
     if (memberExpression == null) 
     { 
      throw new ArgumentException("fieldExpression"); 
     } 

     var field = memberExpression.Member as FieldInfo; 
     if (field == null) 
     { 
      throw new ArgumentException("fieldExpression"); 
     } 

     field.SetValue(this, value); 
    } 

class Foo : PropertyChangedBase { 
    private int _property; 

    public int Property { 
     get { return _property; } 
     set { OnAssignPropertyChanged("Property",() => _property, value); } 
} 

PropertyChangedBaseは、以下の方法でINotifyPropertyChangedを実装

OnAssignPropertyChanged(() => Property, value); 

これが動作する唯一の方法プロパティゲッタのバッキングフィールドを取得してからSetPropertyに渡すことができるかどうかです。プロパティgetメソッドからFieldInfoまたはターゲットメンバーを取得することは可能ですか?

答えて

1

一般的な回答として、少なくとも管理された条件の下では可能です。しかし、あなたがこれを行うべき唯一のケースは、あなたが何をしているのかを確実に確信しているときだけであり、限られたサポートでしか対応できない場合があるからです。

ここで答えを見てください:Find all property references using reflection。ターゲットは少し異なりますが、アプローチはフィールド参照を見つけるために似ています。答えはすでに必要なコードが含まれているので、私はちょうど行く方法の概要を示します:

.Netのすべてのメタデータ項目はトークンによって参照されます。メソッド内で使用されるトークンを取得するには、MethodBody(検査しないものをすべてスキップして)を解析してからresolve the found tokens in their moduleを解析する必要があります。解決するには、ストリームからトークンを読み取るときにBitConverterを使用することを忘れないでください。

しかし今はダウンサイドに。あなたが本当に安全にこれを使ってプロパティゲッターのバッキングフィールドを見つけることができる唯一の時間は、Ldfld、Retなどのようによく定義されたオペコードシーケンスを持つ単純なgetメソッドを見つけるときです。おそらく、C#コンパイラが単純なプロパティと自動実装されたプロパティに対して発行するいくつかのパターンを定義できます。別のものが見つかった場合、他の方法で辞任して例外をスローすることはできません。ゲッターにはコードが含まれている可能性があるからです。 常にリフレクションと同じように、ホワイトリストのアプローチを使用して、必要な条件を確認し、他のケースでは例外を投げるか、遅かれ早かれNullReferenceExceptionを実行します。

これは価値がある場合は決定することですが、一般的には.Net 2.0以来これを行うことができ、ファンシーな外部ライブラリは必要ありません。

+0

合理的に聞こえる。ユースケースは、基底クラスから継承するクラスに非常に特化しています。私たちのコーディングガイドに基づいて、私たちは使用のための規則を強制することができます。ありがとう。 – IAbstract

1

いいえ、一般的にはになりません。あなたはあなたがすることはできません同じPropertyInfo[]FieldInfo[]配列が、異なるバッキングフィールド

+0

もちろん、アクセサーのボディを比較する必要があります。 ILコードがそこにあるように、それは難しいです。リリースモードであなたのサンプルをビルドし、 'typeof(TestSwapped).GetProperty(" PropA ")。GetMethod.GetMethodBody()。GetILAsByteArray()'と 'Test'と似ています。単純にスタックに "this"をロードし、そこから関連するフィールド( 'ldfld')をロードして返します。 'ldfld'命令の引数に違いが見られました。 –

+0

@Jeppe Stig Nielsen:havig * ILコード*、確かに、単純なケース(つまり、return _propB;)でバッキングフィールドを見つけることができます(または少なくとも合理的な推測を提案することもできます)。 *一般的なケース*では、*不可能*(アランチューリング停止定理変更)です。例えば。 'get {if(_propA> 0)}は_propBを返します。 else _propB + 1;} '最初のフィールドが読み込まれました(コンパイラによって異なります)。' _propA'バッキングフィールドは '_propB'です。 –

+0

@DmitryBychenko:プロパティ上の優れた点です。私の実装は単純なgetterに特有のものです: 'get {return _field; } '...プロパティの変更通知を実装しているモデルまたはViewModelは、単純なgetterでなければなりません。 – IAbstract

0

を得るでしょう

public class Test { 
    private int _propA; 
    private int _propB; 

    public int PropA {get { return _propA; }} 
    public int PropB {get { return _propB; }} 
} 

public class TestSwapped { 
    private int _propA; 
    private int _propB; 

    // please, notice swapped backing fields 
    public int PropA {get { return _propB; }} 
    public int PropB {get { return _propA; }} 
} 

:ちょうど2つのクラスを比較します。プロパティは、バッキングフィールドまたはバッキングフィールドのセットを持たないことができます。 偶数セットプロパティには、バッキングフィールドをまったく持たないことができます。

public Int32 Prop 
{ 
set { Debug.WriteLine(value.ToString()); } 
get { return 1; } 
} 

FieldInfoにはどんなものがありますか?

+0

オートアクセッサのプロパティでは、反射を使用して自動バッキングフィールドを取得できます。 { 取得する;セット; }には自動バックフィールドがあります。したがって、私の例のようにgetterが実際にフィールドを返す場合は、バッキングフィールドを取得することができます。 – IAbstract

+0

さて、私の知る限りでは、あなたはできません。 –

+0

それは可能かもしれませんが、コンパイラにとっては巨大な作業です。単純なケースは単純ですが、ケースは簡単に複雑になる可能性があります。 –

0

プロパティは、set/getメソッドまたはミューテータのペアの単なるシンタックスシュガーです。メソッドであれば、空であることを含め、必要なだけのコードを含めることができます。もちろん、コンパイラの観点からバッキングフィールドを持つ必要はありません。

関連する問題