使用 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);
我正在尝试构建一个将调用带有 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);