表达式树:使用 out 或 ref 参数调用方法

Posted

技术标签:

【中文标题】表达式树:使用 out 或 ref 参数调用方法【英文标题】:Expression trees: invoking a method with out or ref arguments 【发布时间】:2011-08-29 20:20:54 【问题描述】:

此代码适用于 .NET4:

class Program

    static void Main( string[] args )
    
        var fooExpr = Expression.Parameter( typeof( Foo ), "f" );
        var parmExpr = Expression.Parameter( typeof( int ).MakeByRefType(), "i" );
        var method = typeof( Foo ).GetMethod( "Method1" );
        var invokeExpr = Expression.Call( fooExpr, method, parmExpr );
        var delegateType = MakeDelegateType( typeof( void ), new[]  typeof( Foo ), typeof( int ).MakeByRefType()  );
        var lambdaExpr = Expression.Lambda( delegateType, invokeExpr, fooExpr, parmExpr );
        dynamic func = lambdaExpr.Compile();
        int x = 4;
        func( new Foo(), ref x );
        Console.WriteLine( x );
    

    private static Type MakeDelegateType( Type returnType, params Type[] parmTypes )
    
        return Expression.GetDelegateType( parmTypes.Concat( new[]  returnType  ).ToArray() );
    


class Foo

    public void Method1( ref int x )
    
        x = 8;
    

此代码不会(在动态调用站点出现运行时错误):

class Program

    static void Main( string[] args )
    
        var fooExpr = Expression.Parameter( typeof( Foo ), "f" );
        var parmExpr = Expression.Parameter( typeof( int ).MakeByRefType(), "i" );
        var method = typeof( Foo ).GetMethod( "Method1" );
        var invokeExpr = Expression.Call( fooExpr, method, parmExpr );
        var delegateType = MakeDelegateType( typeof( void ), new[]  typeof( Foo ), typeof( int ).MakeByRefType()  );
        var lambdaExpr = Expression.Lambda( delegateType, invokeExpr, fooExpr, parmExpr );
        dynamic func = lambdaExpr.Compile();
        int x = 4;
        func( new Foo(), out x );
        Console.WriteLine( x );
    

    private static Type MakeDelegateType( Type returnType, params Type[] parmTypes )
    
        return Expression.GetDelegateType( parmTypes.Concat( new[]  returnType  ).ToArray() );
    


class Foo

    public void Method1( out int x )
    
        x = 8;
    

怎么会?唯一的区别是使用 ref 与 out 参数。

【问题讨论】:

错误是:“委托 有一些无效参数”...没有更多细节,没有内部异常。 出于好奇,如果您在呼叫站点使用ref 而不是out 会发生什么?另外,如果你用正确的out etc 签名声明一个委托类型,并尝试一个静态类型的编译 lambda,会发生什么? 哇,奇怪...如果我在调用站点使用'ref'(并在方法声明中保留'out'),那就可以了。一个错误?编辑 - 再三考虑,不是错误。奇怪的是,没有明确的方法可以在动态构造的委托类型上创建“out”参数。谢谢马克。 【参考方案1】:

Ref 参数将由 CLR 管理,这是一个经典变量,如果这是一个值,它将只是 Box(封装到一个对象中以管理通过引用传递元素)。 p>

Out允许有多个输出,在编译过程中会产生更大的影响。

表达式在运行时编译,但“out”有效性在编译时检查。

通过有效性,我的意思是编译器确保方法 WILL ASSIGN 值给 out 参数。

【讨论】:

【参考方案2】:

你尝试过改变

typeof(int).MakePointerType

代替:

typeof( int ).MakeByRefType()

在行:

var parmExpr = Expression.Parameter( typeof( int ).MakeByRefType(), "i" );
var delegateType = MakeDelegateType( typeof( void ), new[]  typeof( Foo ), typeof( int ).MakeByRefType()  );

问候,

【讨论】:

以上是关于表达式树:使用 out 或 ref 参数调用方法的主要内容,如果未能解决你的问题,请参考以下文章

不能在 lambda 表达式中使用 ref 或 out 参数

Ref 与 Out 的使用方法及区别

ref关键字的用法

ref 与 out

C# ref与out关键字区别

ref的使用