我有一个C#字符串扩展方法,它应该返回一个字符串中子字符串的所有索引的IEnumerable<int>
。它完美地达到了预期的目的,并返回了预期的结果(我的一个测试证明了这一点,但不是下面的那个),但是另一个单元测试发现了它的一个问题:它不能处理null参数。
下面是我正在测试的扩展方法:
public static IEnumerable<int> AllIndexesOf(this string str, string searchText)
{
if (searchText == null)
{
throw new ArgumentNullException("searchText");
}
for (int index = 0; ; index += searchText.Length)
{
index = str.IndexOf(searchText, index);
if (index == -1)
break;
yield return index;
}
}
下面的测试指出了这个问题:
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void Extensions_AllIndexesOf_HandlesNullArguments()
{
string test = "a.b.c.d.e";
test.AllIndexesOf(null);
}
当测试针对我的扩展方法运行时,它会失败,并显示标准错误消息,表明该方法“未抛出异常”。
这很令人困惑:我明确地将null
传递给了函数,但是由于某些原因,比较null == null
返回false
。因此,不会抛出异常,代码会继续执行。
我已经确认这不是测试的bug :在我的主项目中通过调用空比较if
块中的Console.WriteLine
来运行该方法时,控制台上没有显示任何内容,并且我添加的任何catch
块都没有捕获任何异常。此外,使用string.IsNullOrEmpty
而不是== null
也有同样的问题。
为什么这种所谓的简单比较会失败呢?
发布于 2015-05-12 03:36:41
你有一个迭代器块。该方法中的任何代码都不会在返回的迭代器上对MoveNext
的调用之外运行。调用该方法只会创建状态机,而且永远不会失败(除了内存不足错误、堆栈溢出或线程中止异常等极端情况之外)。
当你实际尝试迭代序列时,你会得到异常。
这就是为什么LINQ方法实际上需要两个方法来获得它们所需的错误处理语义。它们有一个私有方法,它是一个迭代器块,然后是一个非迭代器块方法,它只做参数验证(这样它就可以立即完成,而不是被推迟),同时仍然推迟所有其他功能。
所以这就是一般的模式:
public static IEnumerable<T> Foo<T>(
this IEnumerable<T> souce, Func<T, bool> anotherArgument)
{
//note, not an iterator block
if(anotherArgument == null)
{
//TODO make a fuss
}
return FooImpl(source, anotherArgument);
}
private static IEnumerable<T> FooImpl<T>(
IEnumerable<T> souce, Func<T, bool> anotherArgument)
{
//TODO actual implementation as an iterator block
yield break;
}
发布于 2015-06-01 22:07:37
正如其他人所说,枚举器直到它们开始被枚举(即调用IEnumerable.GetNext
方法)时才会被计算。因此这是
List<int> indexes = "a.b.c.d.e".AllIndexesOf(null).ToList<int>();
直到开始枚举时才会进行计算,即
foreach(int index in indexes)
{
// ArgumentNullException
}
https://stackoverflow.com/questions/30176121
复制相似问题