tags:

views:

54

answers:

2

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?

A: 

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.

Les
How can I use toDispose then if its null? It'll throw an argument null exception.
Michael B
If toDispose is passed in as null, then create your own local expression for the variable and assign it. If it is not a null expression, then assume it's bound. If at expression run-time, the toDispose is null, then let it throw an exception.
Les
Then its absolutely useless if its null how do you use it? How does it get disposed? I'm not sure this will work.
Michael B
I don't believe it's useless, but I also don't believe we are communicating well. So disregard this answer and see my next answer.
Les
A: 

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.

Les