MaYaN MaYaN - 9 days ago 6
C# Question

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.

Evk Evk
Answer

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