我正在尝试构建一个将使用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类型?
答案 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);