Blog

C# 8: Using statement

April 23, 2019 | 5 Minute Read

Simplify your using statements.


C# 8.0 Series

Want to read my other posts about C# 8?

Prerequisites & Setup

You will need Visual Studio 2019 and .NET Core 3.0 SDK to try out the new using statement syntax. We need to modify the .csproj file to enable C# 8.0 as well:

<LangVersion>8.0</LangVersion>

A small example

Did you liked my Car class from the previous C# 8 posts? No? That’s okay, I will anyway use it again ;). This time the whole code is little more advanced because we will encrypt and decrypt our car.

class Program
    {
        static void Main(string[] args)
        {
            var car = new Car
            {
                Name = "My awesome car!"
            };

            var key = "MySecret";
            var keyIV = "MyIVSecret";
            byte[] rgbkey;
            byte[] rgbIV;

            using (var pbkdf = new Rfc2898DeriveBytes(key, 10))
            rgbkey = pbkdf.GetBytes(32);

            using (var pbkdf = new Rfc2898DeriveBytes(keyIV, 10))
            rgbIV = pbkdf.GetBytes(16);

            Car decryptedCar = null;
            using (var outStream = new MemoryStream())
            {
                Encrypt(car, outStream, rgbkey, rgbIV);

                // rewind the stream to be readable.
                outStream.Position = 0;

                decryptedCar = Decrypt<Car>(outStream, rgbkey, rgbIV);
            }

            // ...
        }
    }

I hope you aren’t overwhelmed by this - don’t worry I will explain it as good as I can. On the first few lines we create an instance of a Car and generate a secret key and initialization vector for the encryption and decryption.

Please be aware: Never store a secret (passwords, useranmes, keys, ...) in your code! Seriously, it isn't secure in any way!

After that we encrypt the car and put the result into a memory stream named outStream. Then we need to rewind the stream to position zero to ensure we are able to read (decrypt) it. The decrypt function is returning a new instance of the car that must hold the same information, as the first instance. Otherwise we messed up our encrypt or decrypt function (btw. that would be a good candidate for a Unit-Test).

Everyone who reads attentively spotted already that my using statement looks incomplete for prior C# versions (<=7.3). Are you familiar with the concept of stream-chaining? When not, then I show you something new combined with the new using syntax. Let’s see the Encrypt method used above:

public static void Encrypt<T>(T obj, Stream outStream, byte[] rgbKey, byte[] rgbIV)
{
    using var rijndael = Rijndael.Create();
    using var cryptoTransform = rijndael.CreateEncryptor(rgbKey, rgbIV);
    using var cryptoStream = new CryptoStream(outStream, cryptoTransform, CryptoStreamMode.Write, leaveOpen: true);
    using var deflateStream = new DeflateStream(cryptoStream, CompressionLevel.Fastest);
    var binaryFormatter = new BinaryFormatter();
    binaryFormatter.Serialize(deflateStream, obj);
    // disposing deflateStream
    // disposing cryptoStream
    // disposing cryptoTransform
    // disposing rijndael
}

Here, we take the outStream parameter and pass it to the CryptoStream to have everything written to the stream encrypted. The cryptoStream is passed to the DeflateStream to get the stream compressed as well. Finally we use the BinaryFormatter to serialize what ever instance we pass in as obj.

Beside the encryption, stream-chaining and serialization - important is that almost everything is disposable here and ultimately shows how much cleaner the new capability of the using statement looks like. But still maintaining disposing all instances as soon they left the scope. The comments are reflecting the order of how they get disposed.

This new capability doesn’t change the framework or the runtime - it is a pure language feature. Roslyn emits the rest of the using statement for us! IL-Spy reveals this:

il-spy-using-statement

For completeness the Decrypt method:

public static T Decrypt<T>(Stream inStream, byte[] rgbKey, byte[] rgbIV)
{
    using var rijndael = Rijndael.Create();
    using var cryptoTransform = rijndael.CreateDecryptor(rgbKey, rgbIV);
    using var cryptoStream = new CryptoStream(inStream, cryptoTransform, CryptoStreamMode.Read);
    using var deflateStream = new DeflateStream(cryptoStream, CompressionMode.Decompress);
    var binaryFormatter = new BinaryFormatter();
    return (T)binaryFormatter.Deserialize(deflateStream);
}

Closing words

This feature is small, but has enough impact to be worth writing and thinking about it. I love it and I’m sure you will use it too. It removes some tedious steps when we have local disposable resource as shown above especially when we forget the using statement. Writing less code usually increases also readability and this is important. Code is less written than read and therefore it is critical for our colleagues and ourself.