2016-07-13 7 views
7

式ツリーを調べることで、定数、インスタンスフィールド、プロパティの値を取得できますが、メソッドで定義されているローカル変数は取得できません。式ツリー内からローカル変数の値にアクセスする方法

以下を実行すると、定数、インスタンスフィールドとプロパティから1、2、3が出力され、次にGetValueを呼び出すためにFieldInfoが宣言されているインスタンスを取得する方法がわからないため例外が発生します( )をローカル変数に追加します。

using System; 
using System.Linq.Expressions; 
using System.Reflection; 

namespace Example 
{ 
    class Program 
    { 
     private int _intField = 2; 

     static void Main() 
     { 
      new Program().Run(); 
      Console.ReadLine(); 
     } 

     private void Run() 
     { 
      IntProp = 3; 
      var intVariable = 4; 
      Test(() => 1); 
      Test(() => _intField); 
      Test(() => IntProp); 
      Test(() => intVariable); 
     } 

     public int IntProp { get; set; } 

     void Test<T>(Expression<Func<T>> func) 
     { 
      var body = func.Body; 

      if (body.NodeType == ExpressionType.Constant) 
      { 
       Console.WriteLine(((ConstantExpression)body).Value); 
      } 
      else 
      { 
       var memberExpression = body as MemberExpression; 

       var @object = memberExpression.Member.DeclaringType == GetType() 
        ? this 
        : null; //Can I do anything here? Instance of the method the variable is defined in? 

       if (memberExpression.Member.MemberType == MemberTypes.Field) 
       { 
        Console.WriteLine(((FieldInfo)memberExpression.Member).GetValue(@object)); 
       } 
       else if (memberExpression.Member.MemberType == MemberTypes.Property) 
       { 
        Console.WriteLine(((PropertyInfo)memberExpression.Member).GetValue(@object)); 
       } 
      } 
     } 
    } 
} 
+4

これは.NETの私のバージョンで動作します。メソッドが実際に実行されていない限り、変数は存在しません。 –

+2

[反射を介してローカル変数を取得することはできますか?](http://stackoverflow.com/questions/11118084/is-it-possible-to-get-local-variables-through-reflection) – CarbineCoder

+0

あなたは取得しようとしている?パラメータ名は?私はちょっと混乱していますが、必要なものがあればメソッドを呼び出す前に、常にグローバル変数を設定するオプションがあります。 –

答えて

3

ラムダによってキャプチャされ、式ツリーに含まれているローカル変数は、実際にはコンパイラ生成クラスのフィールドになります。

void Test<T>(Expression<Func<T>> func) 
{ 
    var body = func.Body; 

    if (body.NodeType == ExpressionType.Constant) 
    { 
    Console.WriteLine(((ConstantExpression)body).Value); 
    } 
    else 
    { 
    var memberExpression = (MemberExpression)body; 

    var @object = 
     ((ConstantExpression)(memberExpression.Expression)).Value; //HERE! 

    if (memberExpression.Member.MemberType == MemberTypes.Field) 
    { 
     Console.WriteLine(((FieldInfo)memberExpression.Member).GetValue(@object)); 
    } 
    else if (memberExpression.Member.MemberType == MemberTypes.Property) 
    { 
     Console.WriteLine(((PropertyInfo)memberExpression.Member).GetValue(@object)); 
    } 
    } 
} 

はもちろん、あなたはまた、「カンニング」することができます:あなたは、単にこれを行うことはできません

void Test<T>(Expression<Func<T>> func) 
{ 
    Console.WriteLine(func.Compile()()); 
} 
+0

興味深い!しかし、これが答えであれば、その質問が何であるか疑問に思う(つまり、OPがX-Y問題を投稿したと思う)。 –

+0

@MatthewWatsonはい、私は質問をしました。 "この割り当てを' @ object'に変更して、関連するターゲット/インスタンスを見つけるにはどうすればいいですか? "しかし、実際にコンパイラ生成クラスのインスタンスが必要かどうかは不明です(クロージャ(ローカル変数キャプチャ)から取得)。 –

+0

これは私が必要としていたものです。それは私が尋ねたはずの質問です。 – Dan

1

いいえ、できません。

変数はメソッドの値の読み込みに拡張されません。

変数の宣言メタデータのみを処理します。そして、それでもコンパイラはあなたが宣言したと思った変数を削除している可能性があります。すでにプロパティ、フィールドにアクセスすることができます。

+0

コードを読むと、彼が持っているものが_captured_ローカル変数を含む式ツリーであることがわかります。そのようなものはコンパイラ生成クラスのフィールドに変換されています。もちろん、その価値を読み取ることができるはずです。私の答えを見てください。 –

0

これらの変数は、スコープ外のメソッドでは使用できないため、できません。

+0

アスカーコードのローカル変数はラムダ(クロージャ)でキャプチャされているので、確実に利用できます。私の答えを見てください。質問者はあまり説明しなかったので、彼のコードを読む必要がありました。 –

0

いいえあなたはリフレクションではできません。しかし、それを行うための他の方法があります。例として、関数のレキシカルスコープから変数をエクスポートする方法を示します。

は、あなたがこのような方法があるとしましょう:

private void f(int x) 
{ 
    // your code here 
} 

ます。また、このようなコードの部分があります:あなたはgetter関数をエクスポートする必要がありf()外の値を取得するには

int x_in_f; 

f(3); 
x_in_f = /* I want to have the value here */; 

を。 f()voidを返すので、ゲッターを返すことができます。一般的なケース(f()にvoid以外の戻り値型がある場合)outパラメータを利用できます。

を:ここで、両方の変異体である:

private Func<int> f(int x) 
{ 
    // your code here 

    return() => { return x; }; 
} 

又はoutパラメータを介し:

private void f(int x, out Func<int> g) 
{ 
    // your code here 

    g =() => { return x; }; 
} 

コードは次のようになります。次のように

int x_in_f; 
Func<int> g; 

g = f(3); 

x_in_f = g(); // this will return the value of x as it was passed to f() 

又はoutパラメータではf()を呼び出します

f(3, out g); 

その時点で、あなたは他の関数に周りg()を渡すことができます。

private void h(Func<int> getx) 
{ 
    // your code here 
    int x = getx(); 
    // now you have the value of x inside the h() function 
} 

h()と呼び出し:

Func<int> g = f(3); 

// ... 

h(g); 

私はこのことができますか、少なくとも字句回避するためにクロージャを使用する方法を示し願っていますスコープ。

デザイナーのために、これはオブジェクト機能モデルです。 Douglas CrockfordによるThis is a videoのJavascriptでのセキュリティ目的のための使い方について説明しています。上記のように、C#やその他の目的に簡単に変換されます。

関連する問題