Is there a way to tell the compiler that a particular block of code will always be executed?

We have the following construct in our code base, used to ensure that a specific resource is deleted after use:

using (var disposableThing = DisposableThing.Begin())
{
    // do something

    disposableThing.Finish(); // must always be called
}

Here is an example of its use:

List<int> ids;

using (var disposableThing = DisposableThing.Begin())
{
    ids = disposableThing.GetSomeIds();

    disposableThing.Finish();
}

DoSomethingElseWith(ids);

Since this pattern is so common, we wrote a method DisposableThingto encapsulate it:

static void ExecuteWithFinish(Action<DisposableThing> action)
{
    using (var disposableThing = Begin())
    {
        action(disposableThing);

        disposableThing.Finish();
    }
}

which allows us to rewrite the second sample as:

// #4
List<int> ids;

DisposableThing.ExecuteWithFinish(disposableThing =>
{
    ids = disposableThing.GetSomeIds();
});

DoSomethingElseWith(ids); // compiler error "Use of unassigned local variable 'ids'"

But the compiler refuses to compile this code because it does not know what idswill always be assigned after completion ExecuteWithFinish(or throws an exception, which in any case will prevent execution DoSomethingElseWith).

  • I know that I can add an overload ExecuteWithFinishthat returns values ​​from the passed Func, which is ugly.
  • , DisposableThing Dispose Finish, , , (, , ).

", ", , # 4 ?

: , , List<int> ids = null; , () (b) .

+4
2

.

, - Finish(), Dispose(), .

, : Finish() Dispose()? ...

-, Finish():

public interface IDisposableThingWithFinish : IDisposable
{
    void Finish();
}

DisposableThing , IDisposableThingWithFinish.

, Finish(), Dispose() :

public sealed class DisposingFinisher : IDisposable
{
    readonly IDisposableThingWithFinish _item;

    public Disposing(IDisposableThingWithFinish item)
    {
        if (item == null)
            throw new ArgumentNullException(nameof(item));

        _item = item;
    }

    public void Dispose()
    {
        try
        {
            _item.Finish();
        }

        finally
        {
            _item.Dispose();
        }
    }
}

Finisher :

using (var disposableThing = new DisposingFinisher(DisposableThing.Begin()))
{
    // Do something.
}
+5

, CS0165:

List<int> ids = null;
0

Source: https://habr.com/ru/post/1662409/


All Articles