如何将C#方法转换为已编译的表达式?

5

我有以下类层次结构:

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

我需要能够使用反射在任何时候为一个新的Child实例调用这些方法,然而为了提高性能,我需要采用Delegate和/或Compiled Expressions

例如,我可以有:

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

我需要在运行时调用这些方法,并传递需要的参数。请注意,它们包括既有 静态 方法,也有 实例 方法,其中一些接受参数。

到目前为止,我已经尝试了以下内容来调用 "SayInstance" 方法:

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

然后:

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

然而我得到:
1-Say
1-Say

代替:
1-Say 
2-Say

我怀疑这是由于Expression.Constant引起的,但我无法弄清楚如何让它在运行时接受Child实例作为其目标。
当涉及到Expressions时,我感到无助 :-(
基本上,我正在尝试实现Jon Skeet在这里提到的内容,使用DelegatesCompiled Expressions
非常感谢任何帮助。

2
你在构造函数中对 _number 进行了递增操作,而且它是一个静态字段。因此,你永远不会得到预期的结果。请将 _number 字段存储到一个私有字段中。 - Tolgahan Albayrak
1
这只是确保最终结果有效的方法。主要问题在于我无法创建一个在运行时接受Child新实例的表达式。 - MaYaN
1
在调用两次 action(); 的同一个测试中,是否运行了 var instanceOne = new Child();var instanceTwo = new Child(); 这两行代码? - Mat
1
@Mat 他们不必这样做,那只是为了让你更好地了解我想要做什么,但看起来它比有帮助更加混乱!我已经添加了注释,希望能有所帮助。 - MaYaN
2
你正在对同一实例调用一个操作。在调用action()之间,难道不应该创建一个新的instancemCallInstanceaction来获取第二个实例,从而增加你的静态整数吗? - Mikanikal
显示剩余8条评论
2个回答

3
如果我理解正确,您需要使用参数,就像这样:
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

当然,这不会输出1, 2,因为您的_number字段是静态的,在创建两个实例后,它们的值都为2。
编辑。如果需要调用带参数的方法,请声明更多参数。例如,如果SayInstance有一个字符串类型的参数,则:
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");

你,我的朋友,让我开心了!这正是我所需要的。谢谢 :-) - MaYaN
现在只是我贪心了!如果我想要扩展它以传递参数,该怎么办?例如:public void SayInstanceWithArg(string input) - MaYaN
作为参数传递给“action”还是作为常量? - Evk
不是作为常量,而是作为参数,所以对于这个操作是可以的。 - MaYaN
更新的答案,附带一个例子。 - Evk

2

尝试这个,对于无参数构造函数,这对我有用,但这是你需要的:

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
太棒了!已经接近成功了,但是(尽管我的示例)我需要它也能与参数化构造函数一起使用。我已经有了创建对象实例的过程,有没有办法可以传入我已经创建的对象? - MaYaN

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接