Search code examples
c#usingstreamwriter

Unable to apply the new syntax of `using` for StreamWriter


On MSDN about using, there's the new syntax presented. I like it and started to apply it.

Instead of

using (MemoryStream memory = new MemoryStream())
{
  using (CryptoStream crypto = new CryptoStream(memory, transform, CryptoStreamMode.Write))
  {
    using (StreamWriter writer = new StreamWriter(crypto))
    {
      writer.Write(text);
    }
  }
}

I can now go

using Aes aes = Aes.Create();
using ICryptoTransform transform = aes.CreateEncryptor(key, iv);
using MemoryStream memory = new MemoryStream();
using CryptoStream crypto = new CryptoStream(memory, transform, CryptoStreamMode.Write);
using (StreamWriter writer = new StreamWriter(crypto))
  writer.Write(text);

However, I can't make the last part work the new way. For some reason, StreamWriter won't allow me to do this.

using Aes aes = Aes.Create();
using ICryptoTransform transform = aes.CreateEncryptor(key, iv);
using MemoryStream memory = new MemoryStream();
using CryptoStream crypto = new CryptoStream(memory, transform, CryptoStreamMode.Write);
using StreamWriter writer = new StreamWriter(crypto);
writer.Write(text);

I can't really see why. There might be a technical explanation to it in the linked article, as they discuss requirements and limitations. Regrettably, I simply fail to see where they say it and what's implied.

By Skeety's request - full reproducible sample.

public void Test()
{
  string text = "hakuna matata";
  string code = "1234567890abcdef";
  byte[] key = Encoding.ASCII.GetBytes(code);
  byte[] iv = key[..16];

  using Aes aes = Aes.Create();
  using ICryptoTransform transform = aes.CreateEncryptor(key, iv);
  using MemoryStream memory = new MemoryStream();
  using CryptoStream crypto = new CryptoStream(memory, transform, CryptoStreamMode.Write);

  using StreamWriter writer = new StreamWriter(crypto);
  writer.Write(text);
  //writer.Flush();

  //using (StreamWriter writer = new StreamWriter(crypto))
  //  writer.Write(text);

  string output = Convert.ToBase64String(memory.ToArray());
}

Solution

  • Under your old code, all your resources, including the StreamWriter and CryptoStream, get disposed implicitly at the end of their using block. Upon being disposed, any pending content would get flushed to the MemoryStream. Thus, when you inspect the MemoryStream after the end of these using blocks, it would be correctly populated.

    In your new code, the new using syntax means that the resources only get disposed at the end of the enclosing block – in this case, the parent method. Thus, when you inspect the MemoryStream within the same method, you are doing so before StreamWriter and CryptoStream have been disposed, and hence before their contents have been flushed to the MemoryStream.

    For the sake of demonstration, calling Dispose on the StreamWriter just before reading the MemoryStream would restore the correct behavior:

    using StreamWriter writer = new StreamWriter(crypto);
    writer.Write(text);
    writer.Dispose()
    string output = Convert.ToBase64String(memory.ToArray());
    

    That said, I would recommend against the above, since you're mixing explicit Dispose calls with implicit ones generated by the using.

    Alternatively, you could consume the MemoryStream from an outer method, hence ensuring the inner method would have disposed its resources at the end of its execution:

    public void Test()
    {
         var memory = TestInner();
         string output = Convert.ToBase64String(memory.ToArray());
    }
    
    private MemoryStream TestInner()
    {
        string text = "hakuna matata";
        string code = "1234567890abcdef";
        byte[] key = Encoding.ASCII.GetBytes(code);
        byte[] iv = key[..16];
    
        using Aes aes = Aes.Create();
        using ICryptoTransform transform = aes.CreateEncryptor(key, iv);
        using MemoryStream memory = new MemoryStream();
        using CryptoStream crypto = new CryptoStream(memory, transform, CryptoStreamMode.Write);
    
        using StreamWriter writer = new StreamWriter(crypto);
        writer.Write(text); 
    
        return memory;
    }
    

    However, I would prefer using the old using blocks for clarity in situations like this.