首页 > 解决方案 > 带有参数的方法的签名是什么?

问题描述

我正要以Expression.Lambda编程方式绑定(因为未知/可变类型参数),发现如果目标方法使用params,反射调用与直接调用有点不同。

首先,在官方文档中,“签名”(虽然这更像是 XML-doc):

public static
System.Linq.Expressions.Expression<TDelegate>
    Lambda<TDelegate>(
        System.Linq.Expressions.Expression body,
        params System.Linq.Expressions.ParameterExpression[]? parameters
    );

请注意,第二个参数被标记为可选,我们可以写Expression.Lambda<T>(...). 如果在 Visual Studio 中,你转到反汇编的声明列表,你可以看到:

public static
Expression<TDelegate>
    Lambda<TDelegate>(
        Expression body,
        params ParameterExpression[] parameters
    );

第二个参数不再标记为选项。现在,当我尝试使用反射调用此方法时:

    var lambdaFactory = typeof(Expression)
        .GetMethods()
        // Filter overloads
        .Single(x => x.ToString() == "System.Linq.Expressions.Expression`1[TDelegate] Lambda[TDelegate](System.Linq.Expressions.Expression, System.Linq.Expressions.ParameterExpression[])")
        .MakeGenericMethod(myTypeArgument);
    var lambda = (LambdaExpression) lambdaFactory.Invoke(
            null,
            new object[] {
                ...
                // ,null
            }
        );

,我得到了TargetParameterCountException: Parameter count mismatch.。但是如果null作为另一个参数添加,它就可以完美地工作。

这对我来说有点奇怪。为什么 MS Docs 使用?(可选标记)?该params参数是否真的是可选的,类似于常规选项参数,例如string Foo(int a = 1); var result = Foo();?或者它只是一个语法糖?所以这就是为什么我可以直接Expression.Lambda<T>(...)在编辑器中调用,但编译的代码可以不同(这也与反射系统兼容)。如果是这样,这是否意味着该方法总是接收该null值,即使我没有指定值?但是,如果一个方法使用params参数,并且没有传递任何内容,则方法体中的参数是一个有效的数组,带有.Count == 0,而不是null。使用反射传递是否安全null,或者我应该创建一个空对象数组?

标签: c#reflectionsignature

解决方案


文档中的?表示可为空的引用类型,而不是可选参数。可空引用类型纯粹是“编译时检查”的事情,在运行时,它们与常规的旧引用类型没有什么不同。

可空引用类型不是新的类类型,而是对现有引用类型的注释。编译器使用这些注释来帮助您在代码中找到潜在的空引用错误。不可空引用类型和可空引用类型之间没有运行时差异。编译器不会为不可为空的引用类型添加任何运行时检查。好处在于编译时分析。编译器会生成警告,帮助您查找和修复代码中潜在的空错误。您声明您的意图,当您的代码违反该意图时,编译器会警告您。

这解释了为什么?当您查看“反汇编声明列表”时消失。

一切都?告诉你,你可以传递null给这个参数并且该方法仍然有效。

在这种情况下,传递 null 的替代方法是传递一个空数组ParameterExpressions:

var lambda = (LambdaExpression) lambdaFactory.Invoke(
        null,
        new object[] {
            someBodyExpression,
            new ParameterExpression[] { }
        }
    );

这将反映您如何Lambda非反射地调用:

// I'm passing no extra arguments, so an empty array is passed to the "params" parameter
Lambda<T>(someBodyExpression)

推荐阅读