使用 out 参数和可空类型创建表达式

Creating Expression with out parameter and nullable types

我正在尝试构建一个将调用带有 out 参数的方法的表达式。到目前为止,我已经取得了成功,但涉及参数的可为空版本时除外。

为此,让我们假设 int.TryParse(string, out int) 方法。为此,我已经通过定义委托类型成功地构建了一个表达式(无可空值):

internal delegate bool TestDelegate(string input, out int value);

public static MethodInfo GetMethod()
{
    return typeof(int).GetMethod("TryParse", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(string), typeof(int).MakeByRefType() }, null);
}

public static void NormalTestExpression()
{
    var method = GetMethod();

    var pValue = Expression.Parameter(typeof(string), "str");
    var pOutput = Expression.Parameter(typeof(int).MakeByRefType(), "value");

    var call = Expression.Call(method, pValue, pOutput);
    var lamd = Expression.Lambda<TestDelegate>(call, pValue, pOutput);
    var func = lamd.Compile();

    int output;
    var result = func("3", out output);
    // THIS WORKS!!!
}

我正在尝试使用可为 null 的类型进行这项工作。举个例子:

internal delegate bool TestNullableDelegate(string input, out int? value);

下面将因参数异常而失败(GetMethod() 正在检索基于原始类型的正确方法——与上面的方法相同)

public static void WithNullableTypeFails()
{
    var method = GetMethod();

    var pValue = Expression.Parameter(typeof(string), "str");
    var pOutput = Expression.Parameter(typeof(int?).MakeByRefType(), "value");
value
    var call = Expression.Call(method, pValue, pOutput); //Argument Exception int.TryParse() doesn't accept int? argument

    var lamd = Expression.Lambda<TestNullableDelegate>(call, pValue, pOutput);
    var func = lamd.Compile();

}


Expression of type 'System.Nullable`1[System.Int32]' cannot be used for parameter of type 'System.Int32' of method 'Boolean TryParse(System.String, Int32 ByRef)'

现在我知道这是因为我仍在调用采用原始 int 类型的 MethodInfo 并且委托不匹配。所以我尝试了以下:

public static void WithNullableTypeAttempt()
{
    var method = GetMethod();

    var pValue = Expression.Parameter(typeof(string), "str"); 
    var pOutput = Expression.Parameter(typeof(int?).MakeByRefType(), "value");

    var vars = Expression.Variable(typeof(int), "tmp");
    var resultvar = Expression.Variable(typeof(bool), "result");

    var call = Expression.Call(method, pValue, vars);
    var block = Expression.Block(
                        Expression.Assign(vars, Expression.Constant(default(int))), 
                        Expression.Assign(resultvar, call), 
                        Expression.Assign(pOutput, Expression.Convert(vars, typeof(int?))),
                        resultvar
                );
    var lamd = Expression.Lambda<TestNullableDelegate>(block, pValue, pOutput);
    var func = lamd.Compile(); // Invalid Operation Exception
}

当我尝试编译它时遇到无效操作异常:

variable 'tmp' of type 'System.Int32' referenced from scope '', but it is not defined

当我在其中一个表达式树可视化工具中打开表达式时,我看到以下内容:

(valueToParse, value) =>
{
    var tmp = 0;
    var result = int.TryParse(valueToParse, out tmp);
    var value = (int?)tmp;
    return result;
}

所以我认为我走在正确的轨道上。

如何在类型仅因 Nullable 类型而异的情况下调用此方法,同时使委托保持为 Nullable 类型?

您当前使用的 overload of the method 不支持变量。

引用上面的参考资料:

Creates a BlockExpression that contains four expressions and has no variables.

您需要像这样使用 an overload that supports variables 告诉块表达式有关变量的信息:

var block = Expression.Block(
                    new ParameterExpression[] { vars, resultvar }, //variables
                    Expression.Assign(vars, Expression.Constant(default(int))),
                    Expression.Assign(resultvar, call),
                    Expression.Assign(pOutput, Expression.Convert(vars, typeof(int?))),
                    resultvar);