Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I convert C# methods to compiled expressions?

I have the following class hierarchy:

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

I need to be able to invoke any of these methods for a new instance of Child at any given time using reflection however to improve performance I need to resort to Delegate and/or Compiled Expressions.

So for example I can have:

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

for which I would need to at runtime invoke these methods passing the arguments for those that need it. Note they include both static and instance methods with some accepting a parameter.

I have so far tried the following for the "SayInstance" method:

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

And then:

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

However I am getting:

1-Say
1-Say

instead of:

1-Say 
2-Say

I suspect this is due to Expression.Constant but I cannot figure out how I could let it accept an instance of Child as its target at runtime.

I am hopeless when it comes to Expressions :-(

I am basically trying to implement what Jon Skeet mentions HERE either using Delegates or Compiled Expressions.

Any help is very much appreciated.

like image 853
MaYaN Avatar asked Nov 28 '16 20:11

MaYaN


1 Answers

If I understood correctly, you need to use parameters, like this:

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

Of course this will not output 1, 2 because your _number field is static and after creation of two instances has value 2 for both.

EDIT. If you need to call method with arguments - declare more parameters. For example if SayInstance has one argument of type string, then:

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");
like image 75
Evk Avatar answered Oct 13 '22 14:10

Evk