开发者

Is there a way to discover if a ParameterExpression is captured by a BlockExpression or a LambdaExpression

I'm writing some code to help facilitate C# method patterns in expression trees. In the case of the using block, there are three ways to use it::

using(var something=IDisposible_value) //1

using(something = IDisposible_value)   //2

using(something)                 //3

Right now my code looks like this:

public static Expression GenerateUsingBoilerPlate(ParameterExpression disposible,Expression toAssign,Expression body)
{
    ArgumentValidator.AssertIsNotNull(() => disposible);
 ArgumentValidator.AssertIsNotNull(() => body);

    var toDispose = Expression.Variable(typeof(IDisposable));
    Expression retVal = Expression.TryFinally(
        body,
        Expression.Block(
            new[] { toDispose },
            Expression.Assign(
                toDispose,
                Expression.TypeAs(
                    disposible,
                    typeof(IDisposable)
                )
            ),
            Expression.IfThen(
                Expression.NotEqual(
                    toDispose,
                    Expression.Default(
                        typeof(IDisposable)
                    )
                ),
                Expression.Call(
                    toDispose, 
                    "Dispose", 
                    Type.EmptyTypes
         开发者_开发知识库       )
            )
        )
    );
    if (toAssign != null)
    {
        retVal = Expression.Block(
            new[] { disposible },
            Expression.Assign(
                disposible ,
                toAssign
            ),
            retVal
        );
    }
    return retVal;
}

The problem is this code can only handle case 1 and case 3, because I have no way of knowing if the disposible variable is already bound somewhere else in your expression tree. Can anyone suggest a way to find out if the ParameterExpression is bound?


You are creating the variable toDispose (serving as something in your using example) for two of the three cases, but only one of your cases actually declares the variable (case #1). The other two cases assume that the variable is already declared somewhere else. So, can you make toDispose a parameter to your GenerateUsingBoilerPlate method and then if it is null, create toDispose?

This doesn't answer your question on how to determine if the disposible variable is already bound, but can you not simply assume/require that it be bound? Then Case#1 and Case#2 work. Case#3 doesn't use disposible, it instead uses toDispose.

EDIT
In otherwords, you don't need to know if disposible is bound, instead you require it to be bound (if supplied). If not supplied, then you require that toDispose be supplied.


There are actually 4 different ways the using could be called. The examples and numbers below roughly correspond to your numbers except I added #4...

using (var a = File.CreateText(@"c:\temp\test.txt"))  //#1
{
    // a is only visible in this context
}
TextWriter w;
using(w = File.CreateText(@"c:\temp\test.txt")) //#2
{
    // w is visible outside of this context, but is only valid within the context
}
w = File.CreateText(@"c:\temp\test.txt");
using (w)                                       //#3
{
    // w is visible outside of this context, but is only valid between assignment and end of this context
}
using (File.CreateText(@"c:\temp\test.txt"))    //#4
{
    // the disposable is not visible in any context
}

I would recommend that you take a look at the Expression.CatchBlock(...) method and see how the parameters are passed to it. The catch statement is similar in that a local variable is declared to be used within the statement. That might mean that your UsingBoilerPlate might look more like this...

public static Expression<Action> UsingBoilerPlate(
                 Expression disposeExpression,
                 Expression bodyExpression,
                 ParameterExpression localVariable,
                 bool unbound) { ... }
public static Expression<Action> UsingBoilerPlate(
                 Expression disposeExpression,
                 Expression bodyExpression,
                 ParameterExpression localVariable)
{  return UsingBoilerPlate(disposeExpression, bodyExpression, localVariable, true); }
public static Expression<Action> UsingBoilerPlate(
                 Expression disposeExpression,
                 Expression bodyExpression)
{  return UsingBoilerPlate(disposeExpression, bodyExpression, null); }

To handle each of the 4 scenarios, you would call it like this...

var action1 =
    Expression.Lambda<Action>
    (
        Using
        (
            disposableExpression,
            bodyExpression,
            localVariable
        )
    );
action1.Compile()();

var action2 =
    Expression.Lambda<Action>
    (
        Expression.Block
        (
            new [] { localVariable },
            new Expression[] {
                Using
                (
                    disposableExpression,
                    bodyExpression,
                    localVariable,
                    false
                ),
                Expression.IfThenElse(
                    Expression.NotEqual(
                        localVariable, Expression.Constant(null)),
                    ((Expression<Action>)(() => Console.WriteLine("w is NOT null"))).Body,
                    ((Expression<Action>)(() => Console.WriteLine("w is null"))).Body
                )
            }
        )
    );
action2.Compile()();

The next example uses a little trick where using(w) is equivalent to using(w = w)

var action3 =
    Expression.Lambda<Action>
    (
        Expression.Block
        (
            new [] { localVariable },
            new Expression[] {
                Expression.Assign(localVariable, disposeExpression);
                Using
                (
                    localVariable,
                    bodyExpression,
                    localVariable,
                    false
                ),
                Expression.IfThenElse(
                    Expression.NotEqual(
                        localVariable, Expression.Constant(null)),
                    ((Expression<Action>)(() => Console.WriteLine("w is NOT null"))).Body,
                    ((Expression<Action>)(() => Console.WriteLine("w is null"))).Body
                )
            }
        )
    );
action3.Compile()();

And last,

var action4 =
    Expression.Lambda<Action>
    (
        Using
        (
            disposableExpression,
            bodyExpression
        )
    );
action4.Compile()();

So, you can't really figure out if a ParameterExpression is bound external to your method, but you can tell your method whether it is nor not.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜