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

时间:2016-05-24 19:09:49

标签: c# expression-trees

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

为此,我们假设int.TryParse(string, out int)方法。通过为此目的定义委托类型,我已经成功地构建了一个表达式(没有nullables):

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!!!
}

我正在尝试使用可空类型来完成这项工作。举个例子:

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)'

现在我知道这是因为我仍在调用MethodInfo,它采用原始int类型并且代理不匹配。所以我尝试了下面的内容:

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类型?

1 个答案:

答案 0 :(得分:2)

您目前正在使用不支持变量的overload of the method

引自上述参考文献:

  

创建一个包含四个表达式且没有变量的BlockExpression。

您需要使用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);