方法应该抛出异常,但它没有'

本文关键字:抛出异常 方法 | 更新日期: 2023-09-27 18:10:25

我编写了一个小的扩展方法,它在任意IEnumerable中查找给定字符串的索引。

public static IEnumerable<int> FindIndexesOf(this IEnumerable<string> itemList, string indexesToFind)
{
    if (itemList == null)
        throw new ArgumentNullException("itemList");
    if (indexesToFind == null)
        throw new ArgumentNullException("indexToFind");
    List<string> enumerable = itemList as List<string> ?? itemList.ToList();
    for (int i = 0; i < enumerable.Count(); i++)
    {
        if (enumerable[i] == indexesToFind)
            yield return i;
    }
}

正如你在上面看到的,如果itemList为空,就会抛出ArgumentNullException。简单明了。

当在上面的方法上运行我的unittest时,我期望出现ArgumentNullException类型的异常,因为itemList是空的。然而,测试结果为false,因为没有抛出异常。

这怎么可能?逻辑似乎很清楚。请看下面的测试。

[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void FindIndexesOfTest2()
{
    string[] items = null;
    IEnumerable<int> indexes = items.FindIndexesOf("one");
}

我的逻辑哪里出错了;为什么不抛出一个ArgumentNullException?

方法应该抛出异常,但它没有'

问题是使用yield的枚举数是惰性求值的。

由于没有对返回的集合进行迭代,因此该方法并没有实际执行。

正确的方法是将方法分成两部分:

public static IEnumerable<int> FindIndexesOf(this IEnumerable<string> itemList, string indexesToFind)
{
    if (itemList == null)
        throw new ArgumentNullException("itemList");
    if (indexesToFind == null)
        throw new ArgumentNullException("indexToFind");
    return FindIndexesOfImpl(itemList, indexesToFind);    
}
private static IEnumerable<int> FindIndexesOfImpl(this IEnumerable<string> itemList, string indexesToFind)
{
    List<string> enumerable = itemList as List<string> ?? itemList.ToList();
    for (int i = 0; i < enumerable.Count(); i++)
    {
        if (enumerable[i] == indexesToFind)
            yield return i;
    }
}

在这里,第一个方法将在调用时执行,并返回一个延迟求值的枚举数,直到迭代它为止。

尽管如此,我还是建议您将后一种方法改为真正的惰性求值。事实上,该方法缓存整个itemList只是为了能够使用索引,这是不必要的,实际上您可以重写它,而不需要它:

public static IEnumerable<int> FindIndexesOfImpl(this IEnumerable<string> itemList, string indexesToFind)
{
    var index = 0;
    foreach (var item in itemList)
    {
        if (item == indexesToFind)
            yield return index;
        index++;
    }
}

您也可以使用LINQ扩展方法来完成它,尽管这涉及到为每个元素构造一个临时对象,但不确定它是否值得,我将使用上面的方法:

public static IEnumerable<int> FindIndexesOfImpl(this IEnumerable<string> itemList, string indexesToFind)
{
    return itemList
        .Select((item, index) => new { item, index })
        .Where(element => element.item == indexesToFind)
        .Select(element => element.index);
}

使用最后一个方法,您可以将其移回主方法,因为您不再使用yield:

public static IEnumerable<int> FindIndexesOf(this IEnumerable<string> itemList, string indexesToFind)
{
    if (itemList == null)
        throw new ArgumentNullException("itemList");
    if (indexesToFind == null)
        throw new ArgumentNullException("indexToFind");
    return itemList
        .Select((item, index) => new { item, index })
        .Where(element => element.item == indexesToFind)
        .Select(element => element.index);
}