none
构建带有 out ref 参数的表达式 RRS feed

  • 问题

  • 我从老赵的 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
    2011年11月16日 17:33

答案

全部回复

  • Dear,

    链接是不是给错了,打不开,看不到您引用的是那一段。

    就这段代码而言,您最后返回值是不是写错了,怎么能返回自己呢,这样会报StackOverflowException 异常的。还有从代码中没看到哪里有使用ref和out关键字的啊。

     


    Best Regards,
    Rocky Yue[MSFT]
    MSDN Community Support | Feedback to us
    2011年11月18日 2:05
    版主
  • Sorry! 以更新链接和代码
    2011年11月19日 14:49
  • 更新后的代码看上去没问题啊,我测试了,也没出现你说的异常啊。
    Best Regards,
    Rocky Yue[MSFT]
    MSDN Community Support | Feedback to us
    2011年11月21日 1:36
    版主
  • 您确认是反射带有 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
    2011年11月21日 3:04
  • 我觉得可以参考一下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

    2011年11月21日 3:43
    版主
  • - -#

    2011年11月21日 3:48
  • 那样判断不可以吗?
    Best Regards,
    Rocky Yue[MSFT]
    MSDN Community Support | Feedback to us
    2011年11月21日 5:33
    版主
  • 经过再三的测试,我发现只有这一种方式,看下面的代码:

     

     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

    2011年11月22日 3:03
    版主
  • 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
    2011年11月24日 7:26
  • 封装的时候,不是有遍历参数的那一段代码吗?

    我感觉上我们可以在那里做文章,来判断是不是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

    2011年11月24日 7:32
    版主
  • 漂亮的代码.thanks

    2011年11月24日 8:29