Captured variable instantiating problem

2019-08-14 13:55发布

问题:

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();
        }
    }
}

回答1:

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()) {
    // ...
}


回答2:

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
    }


回答3:

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