views:

94

answers:

3

I'm currently musing about some idea I can't get right.

The problem is that I want to use one lambda function to instantiate a captured variable and another lambda to access a property of that variable.

Since the instantiating happens within the lambda the variable isn't actually instantiated the time I want to use it within the second lambda.. this is kind of a chicken and egg problem.

I know that the variable will be instantiated the time it's used in the second lambda but the compiler doesn't.

Is there any way my idea could work? Here's the actual code:

class Program
{
    static void Main(string[] args)
    {
        SqlCommand cmd;

        using (new DisposableComposite(
            () => cmd = new SqlCommand(),
            () => cmd.Connection)) // <- compiler error - variable not instantiated
        {
            // code
        }
    }
}

class DisposableComposite : IDisposable
{
    private List<IDisposable> _disposables = new List<IDisposable>();

    public DisposableComposite(params Func<IDisposable>[] disposableFuncs)
    {
        // ensure the code is actually executed
        foreach (var func in disposableFuncs)
        {
            IDisposable obj = func.Invoke();
            _disposables.Add(obj);
        }
    }

    public void Dispose()
    {
        foreach (var disposable in _disposables)
        {
            disposable.Dispose();
        }
    }
}
+3  A: 

Do you mean just adding:

SqlCommand cmd = null;

(which solves the "definite assignment" glitch; it is definitely assigned... a null ;-p We then update the value before it is used).

IMO, though, you'd do better with nested using statements... and it isn't clear (from the code) where the actual connection is going to come from...

using(var conn = new SqlConnection(...))
using(var cmd = conn.CreateCommand()) {
    // ...
}
Marc Gravell
Damn.. that was an easy one. I guess I need another coffee. :)
VVS
+1  A: 

You can only avoid this by setting cmd to null before the using block:

    SqlCommand cmd=null;

    using (new DisposableComposite(
        () => cmd = new SqlCommand(),
        () => cmd.Connection)) // <- compiler error - variable not instantiated
    {
        // code
    }
Philippe Leybaert
...but will this not lead to cmd.Connection throwing a NullReferenceException? Will that Func delegate "see" the instantiated cmd variable?
Fredrik Mörk
It won't throw an exception, because the assignment in the first delegate will happen first. The delegate will see the variable because of "closures", but that's something that can't be explained in a short comment :)
Philippe Leybaert
@Fredrik: Here's the definition of a captured outer variable: http://en.csharp-online.net/ECMA-334:_14.5.15.3.1_Captured_outer_variables
VVS
Yes, the background of my comment is that I ran a similar test, but it was using strings. That was a bad choice, since they are immutable, which leads to completely different results. Thanks for putting my head back on :)
Fredrik Mörk
A: 

Agree with Marc that this does not really feel right.

Another option would be to define a new Context type object, that on Dispose disposes all the objects it provides.

Eg.

using (var ctx = GetContext()) {
   var cmd = ctx.CreateCommand();
   cmd.Connection = ctx.CreateConnection();
}
// cmd is Disposed 
// cmd.Connection is Disposed
Sam Saffron
Yeah.. this code was inspired by another question (http://stackoverflow.com/questions/966086/using-various-types-in-a-using-statement-c/) and I was just playing around and looking for a better way. I'm not saying that it's actually a better way.
VVS