我正在使用 ReSharper 2024.2,但仍然收到不应使用以下代码的建议:
public async Task<T> GetQueryAsync<T>(string query, bool isToBeValidated = true) where T : IEndPointResponse
{
ArgumentNullException.ThrowIfNullOrEmpty(query);
ArgumentNullException.ThrowIfNullOrWhiteSpace(query);
ArgumentNullException.ThrowIfNull(Logger);
相反,它建议我使用
public async Task<T> GetQueryAsync<T>(string query, bool isToBeValidated = true) where T : IEndPointResponse
{
ArgumentException.ThrowIfNullOrEmpty(query);
ArgumentException.ThrowIfNullOrWhiteSpace(query);
ArgumentNullException.ThrowIfNull(Logger);
或者
public async Task<T> GetQueryAsync<T>(string query, bool isToBeValidated = true) where T : IEndPointResponse
{
if (string.IsNullOrEmpty(query) || string.IsNullOrWhiteSpace(query)) throw new ArgumentNullException(nameof(query));
问题是调用派生类方法 ArgumentException.ThrowIfNullOrEmpty() 将返回 ArgumentException!
这是一个演示我的问题的例子
[TestFixture]
public class Tests
{
readonly MyClass _testClass = new();
#region Passes due to null being passed in and ArgumentNullException being thrown
[Test]
public void Test_MyMethod_Passes()
{
Assert.Throws<ArgumentNullException>(() =>
{
_ = _testClass.Method(null); // passes with ArgumentNullException
});
}
[Test]
public void Test_MyMethodAsync_Passes()
{
Assert.ThrowsAsync<ArgumentNullException>(async () => await _testClass.MethodAsync(null).ToListAsync()); // passes with ArgumentNullException
}
#endregion
#region Fails with ArgumentException being returned instead of ArgumentNullException
[TestCase("")]
[TestCase(" ")]
public void Test_MyMethod(string someText)
{
Assert.Throws<ArgumentNullException>(() => _testClass.Method(someText)); // fails with ArgumentException not ArgumentNullException
}
[TestCase("")]
[TestCase(" ")]
public void Test_MyMethodAsync_Fails(string someText)
{
Assert.ThrowsAsync<ArgumentNullException>(async () => await _testClass.MethodAsync(someText).ToListAsync()); // fails with ArgumentException not ArgumentNullException
}
#endregion
}
public class MyClass
{
public List<string> Method(string? someText)
{
ArgumentNullException.ThrowIfNull(someText);
ArgumentException.ThrowIfNullOrEmpty(someText);
ArgumentException.ThrowIfNullOrWhiteSpace(someText);
var result = new List<string> { "Document1", "Document2" };
return result;
}
public async IAsyncEnumerable<string> MethodAsync(string? someText)
{
ArgumentNullException.ThrowIfNull(someText);
ArgumentException.ThrowIfNullOrEmpty(someText);
ArgumentException.ThrowIfNullOrWhiteSpace(someText);
// Simulate async data fetching
await Task.Delay(100);
yield return "Document1";
yield return "Document2";
}
}
总结
如果我想要 ArgumentNullException,我必须使用 ArgumentNulLException.ThrowIfNullOrEmpty(someText),否则我会得到 ArgumentException。
都不是。您应该明白,如果参数为 null,
ArgumentException.ThrowIfNullOrEmpty
则会抛出。它只是上的静态方法。您“通过”调用它的类型不会改变实现。ArgumentNullException
ArgumentException
想象一下这些方法实际上是这样写的:
现在想象一下你的代码是这样写的:
ReSharper 有效地建议您的代码应写成如下形式:
调用该方法的类型不会决定抛出的异常。唯一不适合进行这种重构的情况是
ArgumentNullException
重新声明ThrowIfNullOrEmpty
- 这会非常糟糕,但事实并非如此。但在这种情况下,“重构”将真正改变调用哪个实现。实际上,调用ArgumentNullException.ThrowIfNullOrEmpty
相当于调用ArgumentException.ThrowIfNullOrEmpty
。它也相当于调用IOException.ThrowIfNullOrEmpty
- 不会抛出IOException
!针对您修改后的代码:您的测试是错误的。忽略不相关的异步和 LINQ 方面,您实际上是在断言当为(即不为空)时
ArgumentException.ThrowIfNullOrEmpty(someText)
将抛出。ArgumentNullException
someText
""
那将会:
ArgumentNullException
(应该只针对空值,而不是空字符串或空格)ArgumentException.ThrowIfNullOrEmpty
(明确指出它会抛出ArgumentException
而不是ArgumentNullException
空字符串ArgumentNullException.ThrowIfNullOrEmpty(someText)
改变。 (仍然会抛出ArgumentException
。)换句话说,您在此处提供的测试仍然会因您的原始代码而失败。 (我已经检查过了。)这是正确的建议。
如果你阅读文档,你会发现其中
ArgumentException.ThrowIfNullOrEmpty
写道:因此,当争论时
null
它仍然会抛出ArgumentNullException
。因此,建议是有效的。