积极答复者
构建带有 out ref 参数的表达式

问题
-
我从老赵的 Fast Reflection Library 提取出构建调用的委托.但是在遇到带有 out ref 修饰的参数引发异常:
System.ArgumentException: 类型为“System.String&”的表达式不能用于方法“System.String PublicReturnWithoutBase_Method(System.String, System.String ByRef)”的类型为“System.String”的参数.
我把构建委托的代码全部封装为一个单独的辅助类. 具体代码链接 : http://fastreflectionlib.codeplex.com/SourceControl/changeset/view/22317#87150
static public Func<Object, Object[], Object> BuildMethodInvokeDelegate(MethodInfo methodInfo) { // 目标 : ((TInstance)instance).Method((T0)parameters[0], (T1)parameters[1], ...) // 执行参数 var instanceParameter = Expression.Parameter(typeof(Object), "instance"); var parametersParameter = Expression.Parameter(typeof(Object[]), "parameters"); // 构建参数列表 var parameterExpressions = new List<Expression>(); var paramInfos = methodInfo.GetParameters(); for (Int32 i = 0; i < paramInfos.Length; i++) { // (Ti)parameters[i] BinaryExpression valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); UnaryExpression valueCast = Expression.Convert(valueObj, paramInfos[i].ParameterType); parameterExpressions.Add(valueCast); } // non-instance for static method, or ((TInstance)instance) var instanceCast = methodInfo.IsStatic
? null
: Expression.Convert(instanceParameter, methodInfo.ReflectedType); // static invoke or ((TInstance)instance).Method var methodCall = Expression.Call(instanceCast, methodInfo, parameterExpressions); // ref out 修饰的参数引发异常. // ((TInstance)instance).Method((T0)parameters[0], (T1)parameters[1], ...) if (methodCall.Type == typeof(void)) { var lambda = Expression.Lambda<Action<Object, Object[]>>( methodCall, instanceParameter, parametersParameter ); Action<Object, Object[]> execute = lambda.Compile(); return (instance, parameters) => { execute(instance, parameters); return null; }; } else { var castMethodCall = Expression.Convert(methodCall, typeof(Object)); var lambda = Expression.Lambda<Func<Object, Object[], Object>>( castMethodCall, instanceParameter, parametersParameter); return lambda.Compile(); } }
不知道应该怎么处理 ref 与 out.
- 已编辑 钱仔 2011年11月19日 14:54
答案
-
经过再三的测试,我发现只有这一种方式,看下面的代码:
var type = typeof(BaseClass); var methodInfo = type.GetMethod("Public_Return_With_ref_Base_Method"); var p1 = Expression.Parameter(type, "program"); var p2 = Expression.Parameter(typeof(string), "pare1"); var p3 = Expression.Parameter(typeof(string).MakeByRefType(), "pare2"); var invokeExpression = Expression.Call(p1, methodInfo, p2,p3); var func = Expression.Lambda<ValidateDelegate>(invokeExpression,p1, p2,p3 ).Compile();
反射中必须和原函数一模一样,如参数列表等。另外你的ValidateDelegate 也得改改才行,否则也是无法编译通过的。看一下官方说明:http://msdn.microsoft.com/zh-cn/library/bb336566.aspx
Best Regards,
Rocky Yue[MSFT]
MSDN Community Support | Feedback to us
- 已编辑 Lie YouModerator 2011年11月22日 3:05
- 已标记为答案 Lie YouModerator 2011年11月24日 6:00
-
封装的时候,不是有遍历参数的那一段代码吗?
我感觉上我们可以在那里做文章,来判断是不是ref 或者out类型的。
官方上只有一个属性可以做这个判断就是IsOut属性:
http://msdn.microsoft.com/zh-cn/library/system.reflection.parameterinfo.isout.aspx
试试看吧。
编辑: 还可以试一下这个:
public static Type GetParameterType(ParameterInfo paramInfo) { Type paramType = paramInfo.ParameterType; return paramType.IsByRef ? paramType.GetElementType() : paramType; }
http://msdn.microsoft.com/zh-cn/library/system.reflection.parameterinfo.parametertype(v=VS.90).aspx
Best Regards,
Rocky Yue[MSFT]
MSDN Community Support | Feedback to us
- 已编辑 Lie YouModerator 2011年11月24日 7:42
- 已标记为答案 钱仔 2011年11月24日 8:31
全部回复
-
Dear,
链接是不是给错了,打不开,看不到您引用的是那一段。
就这段代码而言,您最后返回值是不是写错了,怎么能返回自己呢,这样会报StackOverflowException 异常的。还有从代码中没看到哪里有使用ref和out关键字的啊。
Best Regards,
Rocky Yue[MSFT]
MSDN Community Support | Feedback to us
-
-
您确认是反射带有 ref 或 out 参数的方法吗?
public class BaseClass { #region 特殊方法 public String Public_Return_With_ref_Base_Method(String parameter1, ref String parameter2) { parameter2 = " ref " + parameter2; return "Public_Return_With_ref_Base_Method : " + parameter1 + parameter2; } public String Public_Return_With_out_Base_Method(String parameter1, out String parameter2) { parameter2 = " out parameter2"; return "Public_Return_With_out_Base_Method : " + parameter1 + parameter2; } #endregion } [TestMethod()] public void RefTest() { // 准备 BaseClass source = new BaseClass(); Type sourceType = source.GetType(); var sourceName = "Public_Return_With_ref_Base_Method"; var argumentTypes = new Type[] { typeof(String), typeof(String).MakeByRefType() }; var arguments = new Object[] { "parameter1", "parameter2" }; MethodInfo sourceMethod; Func<Object, Object[], Object> invoker; String expected = "Public_Return_With_ref_Base_Method : parameter1 ref parameter2"; String actual; // 执行 sourceMethod = sourceType.GetMethod(sourceName, BindingFlags.Instance | BindingFlags.Public, null, argumentTypes, null); invoker = ReflectionExpressionBuilder.BuildMethodInvokeDelegate(sourceMethod); actual = (String)invoker(source, arguments); // 断言 Assert.AreEqual(expected, actual); Assert.AreEqual(arguments[1].ToString(), " ref parameter2"); }
测试方法 Fluent.Reflection.TestProject.TempTest.RefTest 引发了异常:System.ArgumentException: 类型为“System.String&”的表达式不能用于方法“System.String Public_Return_With_ref_Base_Method(System.String, System.String ByRef)”的类型为“System.String”的参数
- 已编辑 钱仔 2011年11月21日 3:26
-
-
我觉得可以参考一下IsOut 属性
foreach (var pa in paramInfos) { if (pa.IsOut) { //do something } }
http://msdn.microsoft.com/zh-cn/library/system.reflection.parameterinfo.isout.aspx
Best Regards,
Rocky Yue[MSFT]
MSDN Community Support | Feedback to us
- 已编辑 Lie YouModerator 2011年11月21日 3:44
-
-
经过再三的测试,我发现只有这一种方式,看下面的代码:
var type = typeof(BaseClass); var methodInfo = type.GetMethod("Public_Return_With_ref_Base_Method"); var p1 = Expression.Parameter(type, "program"); var p2 = Expression.Parameter(typeof(string), "pare1"); var p3 = Expression.Parameter(typeof(string).MakeByRefType(), "pare2"); var invokeExpression = Expression.Call(p1, methodInfo, p2,p3); var func = Expression.Lambda<ValidateDelegate>(invokeExpression,p1, p2,p3 ).Compile();
反射中必须和原函数一模一样,如参数列表等。另外你的ValidateDelegate 也得改改才行,否则也是无法编译通过的。看一下官方说明:http://msdn.microsoft.com/zh-cn/library/bb336566.aspx
Best Regards,
Rocky Yue[MSFT]
MSDN Community Support | Feedback to us
- 已编辑 Lie YouModerator 2011年11月22日 3:05
- 已标记为答案 Lie YouModerator 2011年11月24日 6:00
-
Thank you!
请问能否封装一下吗?
我尝试:
...
Type parameterType = paramInfos[i].ParameterType; if (parameterType.IsByRef) { parameterExpressions.Add(Expression.Parameter(parameterType, paramInfos[i].Name)); } else { BinaryExpression valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); UnaryExpression valueCast = Expression.Convert(valueObj, parameterType); parameterExpressions.Add(valueCast); }
...当反射 ref 的时候提示 ref 修饰的参数必须要赋值.
- 已编辑 钱仔 2011年11月24日 7:29
-
封装的时候,不是有遍历参数的那一段代码吗?
我感觉上我们可以在那里做文章,来判断是不是ref 或者out类型的。
官方上只有一个属性可以做这个判断就是IsOut属性:
http://msdn.microsoft.com/zh-cn/library/system.reflection.parameterinfo.isout.aspx
试试看吧。
编辑: 还可以试一下这个:
public static Type GetParameterType(ParameterInfo paramInfo) { Type paramType = paramInfo.ParameterType; return paramType.IsByRef ? paramType.GetElementType() : paramType; }
http://msdn.microsoft.com/zh-cn/library/system.reflection.parameterinfo.parametertype(v=VS.90).aspx
Best Regards,
Rocky Yue[MSFT]
MSDN Community Support | Feedback to us
- 已编辑 Lie YouModerator 2011年11月24日 7:42
- 已标记为答案 钱仔 2011年11月24日 8:31