2016-11-28 52 views
5

mam następujące klasy hierarchia:Jak mogę przekonwertować metody C# na kompilowane wyrażenia?

public class Parent 
{ 
    [DebuggerStepThrough] 
    public void SayParent() 
    { 
     Console.WriteLine("Parent"); 
    } 
} 

public sealed class Child : Parent 
{ 
    private static int _number = 0; 
    public Child() // May contain parameter i.e. not always parameterless consctructor 
    { 
     _number++; 
    } 

    [DebuggerStepThrough] 
    public void SayInstance() 
    { 
     Console.WriteLine("{0}-Say", _number); 
    } 

    [DebuggerStepThrough] 
    public void SayInstanceWithArg(string input) 
    { 
     Console.WriteLine("{0}-Say: {1}", _number, input); 
    } 

    [DebuggerStepThrough] 
    public static void SayStatic() 
    { 
     Console.WriteLine("{0}-Say", _number); 
    } 

    [DebuggerStepThrough] 
    public static void SayStaticWithArg(string input) 
    { 
     Console.WriteLine("{0}-Say: {1}", _number, input); 
    } 

    [DebuggerStepThrough] 
    public static Task SayStaticWithArgAndReturn(string input) 
    { 
     Console.WriteLine("{0}-Say: {1}", _number, input); 
     return null; 
    } 
} 

muszę być w stanie wywołać żadnej z tych metod nowej instancji Child w danym momencie przy użyciu odbicia jednak w celu zwiększenia wydajności muszę uciekać się do Delegate i/lub Compiled Expressions.

Tak na przykład może mam:

var instanceOne = new Child(); 
var instanceTwo = new Child(); 

za które muszę w czasie wykonywania wywołania tych metod przekazując argumenty dla tych, którzy jej potrzebują. Zauważ, że obejmują one metody static i instance z niektórymi akceptującymi parametr.

ja do tej pory próbowałem następujące dla "SayInstance" metody:

var sayInstanceMethod = typeof(Child) 
     .GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static) 
     .Where(m => m.GetCustomAttributes(typeof(DebuggerStepThroughAttribute), true).Length > 0) 
     .Where(t => t.Name == "SayInstance") 
     .First() 

, a następnie:

var instance = Expression.Constant(new Child()); // This should NOT be Constant, but then what should it be?! 
var mCallInstance = Expression.Call(instance, sayInstanceMethod); 

Action action = Expression.Lambda<Action>(mCallInstance).Compile(); 

action(); 
action(); // I need to pass in a new instance of Child to this method somehow 

Jednak ja dostaję:

1-Say 
1-Say 

zamiast:

1-Say 
2-Say 

Podejrzeń, że jest to spowodowane Expression.Constant, ale nie mogę dowiedzieć się, jak mogę pozwolić mu przyjąć wystąpienie Child jako cel w czasie wykonywania.

Jestem beznadziejny, jeśli chodzi o Expressions :-(

jestem zasadniczo próbuje wdrożyć co Jon Skeet wspomina HERE albo za pomocą Delegates lub Compiled Expressions.

Każda pomoc jest bardzo mile widziane.

+2

Przyrostowy numer na konstruktorze. I to jest pole statyczne. Tak więc nigdy nie uzyskasz rezultatu, którego się spodziewałeś. Przechowuj pole _number w polu prywatnym –

+1

To tylko metoda zapewniająca efekt końcowy. Głównym problemem jest to, że nie mogę utworzyć wyrażenia, które akceptuje nową instancję 'Child' w czasie wykonywania. – MaYaN

+1

Czy wiersze 'var instanceOne = new Child();' i 'var instanceTwo = new Child();' uruchomiono w tym samym teście, który dwukrotnie wywołuje funkcję 'action();'? – Mat

Odpowiedz

3

Jeśli dobrze zrozumiałem, trzeba używać parametrów, takich jak to:

var instanceOne = new Child(); 
var instanceTwo = new Child();    
var instance = Expression.Parameter(typeof(Child), "c"); // This should NOT be Constant, but then what should it be?! 
var mCallInstance = Expression.Call(instance, sayInstanceMethod); 
Action<Child> action = Expression.Lambda<Action<Child>>(mCallInstance, instance).Compile(); 

action(instanceOne); 
action(instanceTwo); // I need to pass in a new instance of Child to this method somehow 

Oczywiście nie będzie to wyjście 1, 2 ponieważ pole _liczba jest statyczna i po utworzeniu dwóch przypadkach ma wartość 2 dla obu stron.

EDYCJA. Jeśli potrzebujesz wywołać metodę z argumentami - zadeklaruj więcej parametrów. Na przykład, jeśli SayInstance ma jeden argument typu ciąg, to:

var instanceOne = new Child(); 
var instanceTwo = new Child();    
var instance = Expression.Parameter(typeof(Child), "instance"); 
var arg = Expression.Parameter(typeof(string), "arg"); 
var mCallInstance = Expression.Call(instance, sayInstanceMethod, arg); 
Action<Child,string> action = Expression.Lambda<Action<Child,string>>(mCallInstance, instance, arg).Compile(); 

action(instanceOne, "one"); 
action(instanceTwo, "two"); 
+0

Ty mój przyjacielu, mój dzień! Dokładnie tego potrzebuję. Dziękuję :-) – MaYaN

+0

Po prostu jestem chciwy teraz! jak mogę to rozwinąć, aby również przekazać argument, jeśli chcę? np. 'public void SayInstanceWithArg (wejście łańcuchowe)' – MaYaN

+0

Przekazywać jako argument do "action" lub jako stałą? – Evk

2

Wypróbuj tę pracę dla konstruktora bez parametrów, ale jest to potrzebne:

var instance = Expression.New(typeof(Child).GetConstructor(new Type[0])); 
var mCallInstance = Expression.Call(instance, sayInstanceMethod); 

Action action = Expression.Lambda<Action>(mCallInstance).Compile(); 

action(); 
action(); // I need to pass in a new instance of Child to this method someh 
+1

Amazing! prawie tam jednak (mimo mojego przykładu) potrzebuję go do pracy z sparametryzowanymi konstruktorami, mam już proces tworzenia nowej instancji obiektu, czy istnieje sposób, w którym mogę przekazać obiekt, który już stworzyłem? – MaYaN