0

I can reproduce it using Func<> like so :

public SimilarSyntax<T>(Func<T> f) 
{
    Begin(); 
    var result = f(); 
    End(); 
    return result;
}

However, this isn't quite the same because fcan't assign variables known by the caller.


I would like to be able to replicate using syntax to clean up some code.
Actual code

void SomeProcedure(object param)
{
    Class1 get1;
    Class2 get2;
    ...
    ClassN getN;

    try
    {
        _context.SetReadUnCommitted();
        get1 _context.Get1(param);
        ...

        get2 _context.Get2(param);
    }
    finally
    {
        _context.PutBackOriginalIsolationLevel();
    }

    _context.Get3(param);
}

What I'm attempting to have (or other syntaxe like the one of using)

void SomeProcedure(object param)
{
    Class1 get1;
    Class2 get2;
    ...
    ClassN getN;

    _context.ExecuteInReadUnCommitted
    (
       () =>
        {
            get1 = _context.Get1(param);
            get2 = _context.Get2(param);
        }
    );

    _context.Get3(param);
}

public class Context
{
    public void ExecuteInReadUnCommitted(Action action)
    {
        try
        {
            this.SetReadUnCommitted();
            action();
        }
        finally
        {
            this.PutBackOriginalIsolationLevel();
        }
    }
} 
T.S.
  • 18,195
  • 11
  • 58
  • 78
AXMIM
  • 2,424
  • 1
  • 20
  • 38
  • 1
    A workaround would be to implement a new class with IDisposable, but I feel it's a bit weird to use IDisposable for something else than memory management. – AXMIM Nov 23 '18 at 20:32
  • IDisposable is a contract that a lot of C# programmers have a problem with. Roughly, they never use for the first 2 years, then they get burned badly a couple of times and always use it. Then they learn what it does and use it when it make sense. According to the contract. So you can't predict what's going to happen, if you expect another programmer to use it. If it appears only in your own code then don't worry about it. – Hans Passant Nov 23 '18 at 20:44
  • 1
    Memory management is the one thing `IDisposable` is *not* used for -- we have the garbage collector for that. `IDisposable` is for deterministic cleanup of resources *not* tracked by the runtime. The "deterministically revert context" pattern isn't exactly the same thing as cleaning up resources, but it's a fairly common pattern that `IDisposable` does get used for. If my brain was working I'd cough up an example from the BCL, because I'm pretty sure it has a few. – Jeroen Mostert Nov 23 '18 at 20:46
  • @JeroenMostert You mean like [`WindowsImpersonationContext`](https://learn.microsoft.com/dotnet/api/system.security.principal.windowsimpersonationcontext) and possibly [`DbTransaction`](https://learn.microsoft.com/dotnet/api/system.data.common.dbtransaction)? – Lance U. Matthews Nov 23 '18 at 21:03
  • The using block is inherently just a shortcut for a try...finally block + a null check. It sounds like your Context class should simply implement IDisposeable, with the sole operation of calling `this.PutBackOriginalIsolationLevel()`. By definition of needing that call, it has "some unmanaged resources that require cleanup." Wich is exactly the purpose of IDisposeable. It is even a note to anybody using your Context class (possibly in their classes) to also Implement IDiposeable to relay the Dispose call. – Christopher Nov 23 '18 at 21:03

1 Answers1

2

As others have commented, you can use using and IDisposable to achieve this:

public class Context
{   
    public IDisposable ReadUnCommitted()
    {
        SetReadUnCommitted();
        return Disposable.Create(PutBackOriginalIsolationLevel);
    }
}

And, you can use it as shown here:

using (context.ReadUnCommitted())
{
    get1 = _context.Get1(param);
    // throw new NotImplementedException();
    get2 = _context.Get2(param);
}

get3 = _context.Get3(param);

The Disposble method will always be called, since it is essentially implemented like a try-finally. You can uncomment the throw and verify yourself.

Note: Disposable.Create can be found by using System.Reactive. Or, you can implement your own:

public static class DisposableEx
{
    public static IDisposable Create(Action dispose)
    {
        return new DisposableImpl(dispose);
    }

    private class DisposableImpl : IDisposable
    {
        private readonly Action dispose;

        public DisposableImpl(Action dispose)
        {
            this.dispose = dispose;
        }

        public void Dispose() => dispose?.Invoke();
    }
}
Xiaoy312
  • 14,292
  • 1
  • 32
  • 44