2016-07-13 13 views
7

Badając drzewo wyrażeń, można uzyskać wartość stałej, pole instancji i właściwość, ale nie zmienną lokalną zdefiniowaną w metodzie.Jak uzyskać dostęp do wartości zmiennej lokalnej z drzewa wyrażeń

Wykonanie następujących czynności spowoduje wyprowadzenie 1, 2, 3 (ze stałej, pola instancji i właściwości), a następnie wyjątku, ponieważ nie wiem, jak uzyskać instancję, w której deklarowana jest FieldInfo w celu wywołania GetValue () dla zmiennej lokalnej.

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

Po prostu nie mogę tego zrobić. Zmienna nie istnieje, chyba że metoda faktycznie działa. –

+2

Możliwy duplikat [Czy możliwe jest uzyskanie lokalnych zmiennych za pomocą odbicia?] (Http://stackoverflow.com/questions/11118084/jest-pozwoli-do-get-local-zmiennych-przez-odbrojeniem) – CarbineCoder

+0

Co to jest? próbujesz dostać? Nazwa parametru? Jestem nieco zdezorientowany, ale zawsze masz możliwość ustawienia zmiennej "globalnej" przed wywołaniem metody z czymkolwiek, czego potrzebujesz. –

Odpowiedz

3

Zmienna lokalna, która została przechwycona przez lambda i uwzględniona w drzewie wyrażeń, będzie w tym czasie naprawdę polem w klasie generowanej przez kompilator. To działa na moją wersję .NET:

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)); 
    } 
    } 
} 

Oczywiście, można również „oszukiwać”:

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

Interesujące! Ale jeśli jest to odpowiedź, należy zastanowić się, jakie jest to pytanie (tj. Myślę, że OP opublikował problem X-Y). –

+0

@MatthewWatson Tak, podjąłem pytanie jako "jak zmienić to przypisanie na @ obiekt", aby znaleźć odpowiedni cel/instancję? ". Nie jest jednak jasne, czy naprawdę potrzebuje instancji klasy generowanej przez kompilator (pochodzącej z zamknięcia (przechwytywanie zmiennej lokalnej)). –

+0

To jest dokładnie to, czego potrzebowałem. To jest właśnie pytanie, które powinienem był zadać. – Dan

1

Nie, nie możesz.

Odbicie nie obejmuje odczytania wartości zmiennej metody.

Obsługuje tylko metadane deklaracji zmiennych. Nawet wtedy kompilator mógł usunąć zmienną, o której myślałeś, że ją zadeklarowałeś. Już masz dostęp do właściwości, pól.

+0

Jeśli przeczytasz jego kod, zobaczysz, że to, co ma, jest drzewem wyrażeń zawierającym zmienną lokalną _captured_. Taka rzecz zostanie przekształcona w pole w klasie generowanej przez kompilator. Oczywiście powinieneś być w stanie odczytać jego wartość. Zobacz moją odpowiedź. –

0

Nie, nie można, ponieważ te zmienne są po prostu niedostępne poza metodą ich zakresu.

+0

Zmienna lokalna w kodzie pytającego została przechwycona w lambda (zamknięcie) i jako taka jest na pewno dostępna. Zobacz moją odpowiedź. Pytający nie podał wielu wyjaśnień, więc konieczne było przeczytanie jego kodu. –

0

Nie można z refleksją. Ale są inne metody, aby to zrobić. Jako przykład pokazuję, jak wyeksportować zmienną z zakresu leksykalnego funkcji.

Powiedzmy, że masz sposób tak:

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

Trzeba też kawałek kodu:

int x_in_f; 

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

aby uzyskać wartość spośród f() należy wyeksportować funkcję pochłaniacza. Jako f() zwraca void, po prostu można zwrócić getter. W ogólnym przypadku (gdy f() ma typ zwrotny bez void), można użyć parametru out. Oto oba warianty:

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

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

lub poprzez parametr out:

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

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

Kod będzie wówczas:

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() 

lub poprzez parametr out powołać f() następująco:

f(3, out g); 

W tym momencie można przekazać g() wokół innych funkcji:

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

i inwokacji z h():

Func<int> g = f(3); 

// ... 

h(g); 

Mam nadzieję, że to pomaga lub przynajmniej pokazuje jak używać zamknięć do obejścia leksykalny zakres.

Dla projektantów jest to model obiektowy Model. This is a video Douglas Crockforda, w jaki sposób używać go do celów bezpieczeństwa w JavaScript.Przekłada się to na C# i inne cele z łatwością, jak pokazałem powyżej.

Powiązane problemy