使用重载构造函数从方法名称创建表达式 <Action>

Create Expression<Action> from Method name with overloaded constructor

我正在使用 Hangfire 并且有一种方法可以通过程序集、类型和方法名称来安排作业。使用默认构造函数可以正常工作,但所有方法都有重载的构造函数,这些构造函数将使用 Autofac 激活。

//Works for a default constructor
Type type = Type.GetType("typestring, assemblystring");
var method = type.GetMethod("methodstring");
Expression[] args = new Expression[] { Expression.Constant(options,typeof(Options)) }; //All methods use the same parameters
var action = Expression.Lambda<Action>(Expression.Call(Expression.New(type), method, args));
RecurringJob.AddOrUpdate(action, "cronstring");

尝试修改以支持重载构造函数(无默认值)我有此代码。

Type type = Type.GetType("typestring, assemblystring");
var method = type.GetMethod("methodstring");
Expression[] args = new Expression[] { Expression.Constant(options,typeof(Options)) }; //All methods use the same parameters
var ctor = type.GetConstructors().ToList().FirstOrDefault();
var ctorParams = ctor.GetParameters();
var ctorArgs = new Expression[ctorParams.Length];
for (int i = 0; i != ctorParams.Length; ++i)
{
   ParameterExpression param = Expression.Parameter(typeof(object), ctorParams[i].Name);
   ctorArgs[i] = Expression.Convert(param, ctorParams[i].ParameterType);
}            
var ctorExpress = Expression.New(ctor, ctorArgs);
var action = Expression.Lambda<Action>(Expression.Call(ctorExpress, method, args));
RecurringJob.AddOrUpdate(action, "cronstring"); 

我收到此错误:InvalidOperationException:'System.Object' 类型的变量“{first constructor param}”从范围“”引用,但未定义

我不确定我是不是遗漏了什么或者是我做错了。我使用表达式的经验有限。

如果我没记错的话,还有另一个代码可以编译你的表达式并调用它,作为参数提供 object[],你必须按顺序将这些参数放入构造函数中,然后你可以使用示例代码如下:

static void Main(string[] args)
{
    Type type = typeof(Foo);
    var ctor = type.GetConstructor(new[] { typeof(int), typeof(string) });
    var ctorParams = ctor.GetParameters();
    var dynPar = Expression.Parameter(typeof(object[]), "d");
    var ctorArgs = new Expression[ctorParams.Length];
    for (int i = 0; i != ctorParams.Length; ++i)
    {
        var indVal = Expression.ArrayIndex(dynPar, Expression.Constant(i));
        ctorArgs[i] = Expression.Convert(indVal, ctorParams[i].ParameterType);
    }
    //{ new Foo(Convert(d[0], Int32), Convert(d[1], String))}
    var ctorExpress = Expression.New(ctor, ctorArgs);
    //{ new Foo(Convert(d[0], Int32), Convert(d[1], String)).Run()}
    var callRun = Expression.Call(ctorExpress, type.GetMethod("Run"));
    //{ d => new Foo(Convert(d[0], Int32), Convert(d[1], String)).Run()}
    var action = Expression.Lambda<Action<object[]>>(callRun, dynPar);
    action.Compile()(new object[] { 1, "a" });
}

public class Foo
{
    public Foo(int a, string b)
    {
        A = a;
        B = b;
    }
    public int A { get; }
    public string B { get; }
    public void Run()
    {
        Console.WriteLine($"It is {A} and {B}");
    }
}

谢谢@ASpirin,您的意见帮助解决了这个问题。我将构造函数的参数更改为 Constant 而不是 Parameter 并传入 null.

Type type = Type.GetType("typestring, assemblystring");
var method = type.GetMethod("methodstring");
Expression[] args = new Expression[] { Expression.Constant(options,typeof(Options)) }; //All methods use the same parameters
var ctor = type.GetConstructors().ToList().FirstOrDefault();
var ctorParams = ctor.GetParameters();
var ctorArgs = new Expression[ctorParams.Length];
for (int i = 0; i != ctorParams.Length; ++i)
{
   var param = Expression.Constant(null,typeof(object)); //Updated this line
   ctorArgs[i] = Expression.Convert(param, ctorParams[i].ParameterType);
}            
var ctorExpress = Expression.New(ctor, ctorArgs);
var action = Expression.Lambda<Action>(Expression.Call(ctorExpress, method, args));
RecurringJob.AddOrUpdate(action, "cronstring");

这是可行的,因为 Hangfire 实际上并不执行构造函数并像这样保存表达式:

//Class name is Test, method name is Run
var test = Activate<Test>();
test.Run(FromJson<Options>("REMOVED"));

Activate<Test> 在另一个程序中使用 Autofac 激活。