如何获取局部变量的表达式

How to get expression for a local variable

我正在制作一个支持插件的框架,因此它会在应用程序启动时加载插件 dll 中定义的 类。

foreach (Type t in assembly.GetTypes()) {
    if (t.IsAssignableTo(typeof(AIGameBodyBehaviour))) {
        AIGameBodyBehaviourFactory factory = new AIGameBodyBehaviourFactory() {
            Id = t.Name,
            NewAIGameBodyBehaviour = Expression.Lambda<Func<AIGameBodyBehaviourFactoryArguments, AIGameBodyBehaviour>>(
                    Expression.New(
                        t.GetConstructor(new Type[] {typeof(AIGameBodyBehaviourFactoryArguments) }), 
                        new Expression[] {
                            //stuck at here
                        }
                    ),
                    new ParameterExpression[] {
                        Expression.Parameter(typeof(AIGameBodyBehaviourFactoryArguments), "arguments")
                    }
                ).Compile()
        };
        EntityLoaderInterface.AddAIGameBodyBehaviourFactory(factory);
    } 
}

如上所示,complied lambda 采用 AIGameBodyBehaviourFactoryArguments 类型的参数和 return AIGameBodyBehaviour 的实例。 lambda函数体是对用户定义的AIGameBodyBehaviour子类的构造函数的调用。

lambda表达式的参数名为“arguments”。这个名称应该被重新用作表示要传递给构造函数的值的表达式。但是,即使在检查了 the documentation provided by Microsoft 并通过 Google 进行了一些研究之后,我仍然找不到如何执行此操作。

当你被表达式卡住的时候,可以考虑SharpLab。如果我理解正确的话,你想要一个看起来像这样的表达式:

Expression<Func<AIGameBodyBehaviourFactoryArguments, AIGameBodyBehaviour>> expression =
            (arguments) => new AIGameBodyBehaviour(arguments);

SharpLab 会告诉你编译器会将这一行变成

ParameterExpression parameterExpression = Expression.Parameter(typeof(AIGameBodyBehaviourFactoryArguments), "arguments");
ConstructorInfo constructor = (ConstructorInfo)MethodBase.GetMethodFromHandle((RuntimeMethodHandle)/*OpCode not supported: LdMemberToken*/);
Expression[] array = new Expression[1];
array[0] = parameterExpression;
NewExpression body = Expression.New(constructor, (IEnumerable<Expression>)array);
ParameterExpression[] array2 = new ParameterExpression[1];
array2[0] = parameterExpression;
Expression<Func<AIGameBodyBehaviourFactoryArguments, AIGameBodyBehaviour>> expression = Expression.Lambda<Func<AIGameBodyBehaviourFactoryArguments, AIGameBodyBehaviour>>(body, array2);

(See Sharplab)

您可以看到 ParameterExpression 的相同实例用作 Expression.New 中的第二个参数和 Expression.Lambda 中的第二个参数。因此,您的代码应如下所示:

foreach (Type t in assembly.GetTypes()) {
    if (t.IsAssignableTo(typeof(AIGameBodyBehaviour))) {
        var parameterExpression = Expression.Parameter(typeof(AIGameBodyBehaviourFactoryArguments), "arguments");
        AIGameBodyBehaviourFactory factory = new AIGameBodyBehaviourFactory() {
            Id = t.Name,
            NewAIGameBodyBehaviour = Expression.Lambda<Func<AIGameBodyBehaviourFactoryArguments, AIGameBodyBehaviour>>(
                    
                    Expression.New(
                        t.GetConstructor(new Type[] {typeof(AIGameBodyBehaviourFactoryArguments) }), 
                        new Expression[] {
                            parameterExpression 
                        }
                    ),
                    new ParameterExpression[] {
                        parameterExpression 
                    }
                ).Compile()
        };
        EntityLoaderInterface.AddAIGameBodyBehaviourFactory(factory);
    } 
}