简体   繁体   English

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

[英]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 . 我需要能够在任何给定时间使用反射为Child的新实例调用这些方法中的任何一种,但是为了提高性能,我需要求助于Delegate和/或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. 请注意,它们包括static方法和instance方法,并带有一些接受参数的方法。

I have so far tried the following for the "SayInstance" method: 到目前为止,我已经为“ 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()

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. 我怀疑这是由于Expression.Constant引起的,但我无法弄清楚如何让它在运行时接受Child实例作为其目标。

I am hopeless when it comes to Expressions :-( 对于Expressions我无可救药:-(

I am basically trying to implement what Jon Skeet mentions HERE either using Delegates or Compiled Expressions . 我基本上是想实现什么乔恩斯基特提到这里或者使用DelegatesCompiled Expressions

Any help is very much appreciated. 很感谢任何形式的帮助。

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. 当然,这不会输出1, 2因为您的_number字段是静态的,并且在创建两个实例后,两个实例的值均为2。

EDIT. 编辑。 If you need to call method with arguments - declare more parameters. 如果您需要使用参数调用方法-请声明更多参数。 For example if SayInstance has one argument of type string, then: 例如,如果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");

Try this out, this workf for me for a parameterless constructor, but this is that you need: 试试这个,对于没有参数的构造函数来说,这对我来说是工作,但这是您需要的:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM