尽管有 using 语句,异常过滤器仍会导致 CA2000
Exception filter causes CA2000 despite a using statement
以下代码是我们部分生产代码的简化摘录。它计算一个文件的 SHA256 散列值,returns 它作为一个字符串,或者 returns 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 是误报,还是异常过滤器创建了一个不会发生处置的场景?
看起来这是一个 false-positive 并且可以安全地压制。
我比较了两种版本方法的中间语言。它们都将 using
语句显示为正确处理对象的 try/finally
块。事实上,这两种方法的 IL 是相同的,除了外部 catch/exception 过滤器部分。
.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 ...
以下代码是我们部分生产代码的简化摘录。它计算一个文件的 SHA256 散列值,returns 它作为一个字符串,或者 returns 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 是误报,还是异常过滤器创建了一个不会发生处置的场景?
看起来这是一个 false-positive 并且可以安全地压制。
我比较了两种版本方法的中间语言。它们都将 using
语句显示为正确处理对象的 try/finally
块。事实上,这两种方法的 IL 是相同的,除了外部 catch/exception 过滤器部分。
.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 ...