【问题标题】:Replace nested using-statement with one using statement用一条 using 语句替换嵌套的 using 语句
【发布时间】:2018-11-18 17:52:11
【问题描述】:

我发现自己在重复这段代码

using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read)
{
  using (var aes = AesCryptoServiceProvider() { Key = ... }
  {

    // Read the IV at the beginning of the filestream

    using (var cryptoStream = new CryptoStream(fileStream, aes.CreateDecryptor(), CryptoStreamMode.Read)
    {

      // Actual code only using cryptoStream

    }
  }
}

using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write)
{
  using (var aes = AesCryptoServiceProvider() { Key = ... }
  {

    // Write the IV at the beginning of the filestream

    using (var cryptoStream = new CryptoStream(fileStream, aes.CreateDecryptor(), CryptoStreamMode.Write)
    {

      // Actual code only using cryptoStream

    }
  }
}

我问自己,是否可以用类似的东西代替它

using (var cryptoStream = new MyDecryptionStream(path))
{
  // Actual code
}

实际代码可能非常不同。它可以是必须保存的图像或 xml 序列化。

我尝试实现自己的 Stream 类,它将所有方法转换为私有属性 CryptoStream。但这没有成功。它总是在对应的地方坏掉,我一开始就试图阅读 IV。

【问题讨论】:

  • 请注意,您并不总是需要所有 {}。在没有中间 { 的情况下做 using () /* new line*/ using () /* new line */ using () { 被认为是美学上正确的。 Visual Studio 将在同一级别格式化所有using
  • 是的,可以通过在 MyDecryptionStream 上实现 IDisposable。如果没有代码和不同的问题,很难说出你没有成功的原因。
  • 那么你的实现是什么,具体来说,它是如何不起作用的?如果您没有展示它并且没有描述它有什么问题,我们无法告诉您如何解决它。
  • 别忘了using 只是IDisposables 的语法糖。这是使用产生的,以消除任何魔法:docs.microsoft.com/en-us/dotnet/csharp/language-reference/…。因此,答案是,是的,您可以通过创建一个IDisposable MyDecryptionStream 来正确管理底层FileStreamAesCryptoServiceProviderCryptoStream 的使用和处置。我建议您尝试一下,并在实施 IDisposable 时遇到问题回到 SO。

标签: c# using-statement


【解决方案1】:

这是一个非常粗略的示例,说明您正在尝试做的事情。有很多地方可以改进,但它是一个工作示例,您可以希望以此为基础。

首先,我们创建一个实现IDisposable 的类。这允许我们在using 语句中使用这个类。该类将实例化我们需要的其他三个对象,并自行处理它们。

class MyCryptoStream : IDisposable
{
    private FileStream fileStream = null;
    private AesCryptoServiceProvider aes = null;
    public CryptoStream cryptoStream = null;

    public enum Mode
    {
        Write,
        Read
    }

    public MyCryptoStream(string filepath, Mode mode, byte[] key, byte[] iv = null)
    {
        if(mode == Mode.Write)
        {
            fileStream = new FileStream(filepath, FileMode.Open, FileAccess.Write);
            fileStream.Write(iv, 0, 16);
            aes = new AesCryptoServiceProvider() { Key = key, IV = iv };

            cryptoStream = new CryptoStream(fileStream, aes.CreateEncryptor(), CryptoStreamMode.Write);
        }
        else
        {
            iv = new byte[16];
            fileStream = new FileStream(filepath, FileMode.Open, FileAccess.Read);
            fileStream.Read(iv, 0, 16);
            aes = new AesCryptoServiceProvider() { Key = key, IV = iv };

            cryptoStream = new CryptoStream(fileStream, aes.CreateDecryptor(), CryptoStreamMode.Read);
        }
    }

    #region IDisposable Support
    private bool disposedValue = false; // To detect redundant calls

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                if (cryptoStream != null)
                {
                    cryptoStream.Dispose();
                }
                if (aes != null)
                {
                    aes.Dispose();
                }
                if (fileStream != null)
                {
                    fileStream.Dispose();
                }
            }

            // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
            // TODO: set large fields to null.

            disposedValue = true;
        }
    }

    // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
    // ~UsingReduction() {
    //   // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
    //   Dispose(false);
    // }

    // This code added to correctly implement the disposable pattern.
    public void Dispose()
    {
        // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
        Dispose(true);
        // TODO: uncomment the following line if the finalizer is overridden above.
        // GC.SuppressFinalize(this);
    }
    #endregion

}

现在,我们可以像这样使用这个类:

        string path = "..\\..\\test.txt";
        byte[] key = null;
        byte[] iv = null;
        using (AesCryptoServiceProvider myAes = new AesCryptoServiceProvider())
        {
            key = myAes.Key;
            iv = myAes.IV;
        }
        using (MyCryptoStream ur = new MyCryptoStream(path, MyCryptoStream.Mode.Write, key, iv))
        {
            using (StreamWriter sw = new StreamWriter(ur.cryptoStream))
            {
                sw.Write("Test string");
            }
        }
        string text = string.Empty;
        using (MyCryptoStream ur = new MyCryptoStream(path, MyCryptoStream.Mode.Read, key))
        {
            using (StreamReader sr = new StreamReader(ur.cryptoStream))
            {
                text = sr.ReadToEnd();
            }
        }

如果您运行此示例,您可以看到它使用cryptostream"Test string" 写入文件,然后从该文件中读取相同的文本。查看text的值,可以看到还是"Test string",说明程序成功了。

【讨论】:

    【解决方案2】:

    辅助函数呢?

    public static TResult ReadFileUsingCrypto<TResult>(string path, KeyThing key, Func<CryptoStream, TResult> use)
    {
        using (var fileStream = new FileStream(path, FileMode.Open, FileAccesa.Read))
        using (var aes = new AesCryptoServiceProvider(){...}))
        using (var cryptoStream = new CryptoStream(fileStream, aes.CreateDecryptor(), CryptoStreamMode.Read))
        {
            return use(cryptoStream);
        }
    }
    

    然后

    var result = ReadFileUsingCrypto(“myFile”, key, crypto => <use crypto here and return result>);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-21
      • 1970-01-01
      • 2010-11-22
      • 2019-02-05
      • 2014-06-05
      • 2014-04-04
      • 2011-12-27
      相关资源
      最近更新 更多