2

以下代码是我们生产代码的一部分的简化摘录。它计算文件的 SHA256 哈希值并将其作为字符串返回,或者null在文件无法访问时返回:

private static string CalculateHash(string fileName)
{
    try
    {
        string result;
        using (SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider())
        {
            byte[] data = File.ReadAllBytes(fileName);
            result = BitConverter.ToString(sha256.ComputeHash(data));
        }

        Debug.WriteLine("Calculated hash for '" + fileName + "': " + result, 3);
        return result;
    }
    catch (UnauthorizedAccessException ex)
    {
        Debug.WriteLine("The hash calculation failed: " + ex.Message, 3);
        return null;
    }
    catch (IOException ex)
    {
        Debug.WriteLine("The hash calculation failed: " + ex.Message, 3);
        return null;
    }
}

我们的一位开发人员最近使用异常过滤器重构了代码以减少重复catch块,所以它现在看起来像这样:

private static string CalculateHash(string fileName)
{
    try
    {
        string result;
        using (SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider())
        {
            byte[] data = File.ReadAllBytes(fileName);
            result = BitConverter.ToString(sha256.ComputeHash(data));
        }

        Debug.WriteLine("Calculated hash for '" + fileName + "': " + result, 3);
        return result;
    }
    catch (Exception ex) when (ex is UnauthorizedAccessException || ex is IOException)
    {
        Debug.WriteLine("The hash calculation failed: " + ex.Message, 3);
        return null;
    }
}

然而,我们现在得到一个代码分析警告:

CA2000 - 在方法“CalculateHash(string)”中,在对对象“sha256”的所有引用超出范围之前调用 System.IDisposable.Dispose。

据我所知,SHA256CryptoServiceProvider这里的处理是正确的,无论异常是否被过滤器捕获,都会发生这种情况。

这个 CA2000 是误报,还是异常过滤器创建了一个不会发生处置的场景?

4

1 回答 1

0

看起来这是一个误报,可以安全地抑制。

我已经比较了该方法的两个版本的中间语言。它们都将using语句显示try/finally为正确处理对象的块。事实上,这两种方法的 IL 是相同的,除了外部捕获/异常过滤器部分。

 .try
 {
   IL_0000: newobj       instance void [System.Core]System.Security.Cryptography.SHA256CryptoServiceProvider::.ctor()
   IL_0005: stloc.1   V_1
   .try
   {
     IL_0006: ldarg.0   fileName
     IL_0007: call         unsigned int8[] [mscorlib]System.IO.File::ReadAllBytes(string)
     IL_000c: stloc.2   'buffer [Range(Instruction(IL_000c stloc.2)-Instruction(IL_000e ldloc.2))]'
     IL_000d: ldloc.1   V_1
     IL_000e: ldloc.2   'buffer [Range(Instruction(IL_000c stloc.2)-Instruction(IL_000e ldloc.2))]'
     IL_000f: callvirt     instance unsigned int8[] [mscorlib]System.Security.Cryptography.HashAlgorithm::ComputeHash(unsigned int8[])
     IL_0014: call         string [mscorlib]System.BitConverter::ToString(unsigned int8[])
     IL_0019: stloc.0   'string [Range(Instruction(IL_0019 stloc.0)-Instruction(IL_0026 ldloc.0))]'
     IL_001a: leave.s      IL_0026
   } // end of .try
   finally
   {
     IL_001c: ldloc.1   V_1
     IL_001d: brfalse.s    IL_0025
     IL_001f: ldloc.1   V_1
     IL_0020: callvirt     instance void [mscorlib]System.IDisposable::Dispose()
                        /* ^^ here we can see the Dipose method being called
                         *    in the finally block 
                         */
     IL_0025: endfinally   
   } // end of finally
   IL_0026: ldloc.0   'string [Range(Instruction(IL_0019 stloc.0)-Instruction(IL_0026 ldloc.0))]'
   IL_0027: stloc.3   V_3
   IL_0028: leave.s      IL_0034
 } // end of .try
 // ... catch or exception filter IL code then appears here ...
于 2017-02-06T12:17:41.390 回答