Using() block hell in C#

So the other day I was working on transforming streams for an encryption layer for work. I ended up having some code that looked similar to this:

public async Task SendAsync(byte[] data, Delegate next)
{
    using (var stream = ...)
    {
        using (var encryptStream = ...)
        {
            using (var encodeStream = ...)
            {
                using (var reader = ...)
                {
                    await next(...);
                }
            }
        }
    }
}


public async Task RecieveAsync(byte[] data, Delegate next)
{
    using (var stream = ...)
    {
        using (var decodeStream = ...)
        {
            using (var decryptStream = ...)
            {
                using (var reader = ...)
                {
                    await next(...);
                }
            }
        }
    }
}

Which doesn’t look very great! There’s a nice solution relying on next-line context statements showcased over at stackoverflow but my colleagues and I agreed that “hack” doesn’t necessarily get us what we want. There’s a reason most people don’t do:

if (this)
if (andThat)
if (andKitchenSink)
{
    // Do stuff
}

It’s just as ugly in a non-aesthetic way.

Ok, what about just using try/finally?

public async Task SendAsync(byte[] data, Delegate next)
{
    IDisposable stream = null, encryptedStream = null, encodeStream = null;
    StreamReader reader = null;

    try {
        stream = ...;
        encryptStream = ...;
        encodeStream = ...;
        reader = ...;
        await next(...);
    }
    finally
    {
        stream?.Dispose();
        encryptedStream?.Dispose();
        encodedStream?.Dispose();
        reader?.Dispose();
    }
}


public async Task RecieveAsync(byte[] data, Delegate next)
{
    var stream = null, decodeStream = null, decryptStream = null;
    StreamReader reader = null;
    try {
        stream = ...;
        decodeStream = ...;
        decryptStream = ...;
        reader = ...;
        await next(...);
    }
    finally
    {
        stream?.Dispose();
        decodeStream?.Dispose();
        decryptStream?.Dispose();
        reader?.Dispose();
    }    
}

But…that’s equally bad to look at. So I threw together a builder pattern that makes the above look something like this:

public async Task SendAsync(byte[] data, Delegate next)
{
    With.Using(() => new ...)
        .Using(memoryStream => ...)
        .Using(cryptoStream => ...)
        .Using(base64Stream => ...)
        .Then(async streamReader => await next(...));
}

public async Task RecieveAsync(byte[] data, Delegate next)
{
    With.Using(() => new ...)
        .Using(memoryStream => ...)
        .Using(base64Stream => ...)
        .Using(cryptoStream => ...)
        .Then(async streamReader => await next(...));
}

I think it looks pretty clean? Obviously this isn’t something you just throw into a JobCorpCo code base but it’s a fun exercise in what you can do with code. If you’re interested, here are the helper classes.

 
0
Kudos
 
0
Kudos

Now read this

VSCode, GDB, and Debugging an OS

I’ve been spending some time going through Philipp Oppermann’s amazing blog series, Writing an OS in Rust. Digging into operating systems has been on my todo list for years now and I couldn’t pass up the opportunity when I ran across... Continue →