泛型列表<T>作为IEnumerable<object>。

3

我希望将一个List转换为IEnumerable,以便验证不同的列表不是null或空的:

假设myList是一个List <T>。那么在调用代码中,我想要:

       Validator.VerifyNotNullOrEmpty(myList as IEnumerable<object>,
                                     @"myList",
                                     @"ClassName.MethodName");

验证代码如下:
     public static void VerifyNotNullOrEmpty(IEnumerable<object> theIEnumerable,
                                        string theIEnumerableName,
                                        string theVerifyingPosition)
    {
        string errMsg = theVerifyingPosition + " " + theIEnumerableName;
        if (theIEnumerable == null)
        {
            errMsg +=  @" is null";
            Debug.Assert(false);
            throw new ApplicationException(errMsg);

        }
        else if (theIEnumerable.Count() == 0)
        {
            errMsg +=  @" is empty";
            Debug.Assert(false);
            throw new ApplicationException(errMsg);

        }
    }

然而,这并不起作用。它可以编译,但是IEnumerable为空!为什么?

这些答案有帮到您吗? - Dave D
@Dave - 哎呀,不好意思。非常感谢你的回答。我选择了Heinzi的答案,因为它更专注于解释我的代码为什么不起作用,但是你的回答清楚地向我解释了应该怎么做。谢谢! - Avi
3个回答

6

List实现了IEnumerable接口,所以您不需要进行强制转换,您只需要将方法接受一个泛型参数即可,如下所示:

 public static void VerifyNotNullOrEmpty<T>(this IEnumerable<T> theIEnumerable,
                                    string theIEnumerableName,
                                    string theVerifyingPosition)
{
    string errMsg = theVerifyingPosition + " " + theIEnumerableName;
    if (theIEnumerable == null)
    {
        errMsg +=  @" is null";
        Debug.Assert(false);
        throw new ApplicationException(errMsg);

    }
    else if (theIEnumerable.Count() == 0)
    {
        errMsg +=  @" is empty";
        Debug.Assert(false);
        throw new ApplicationException(errMsg);

    }
}

您只需要调用它:

var myList = new List<string>
{
    "Test1",
    "Test2"
};

myList.VerifyNotNullOrEmpty("myList", "My position");

你可以稍微改进一下实现方式:
 public static void VerifyNotNullOrEmpty<T>(this IEnumerable<T> items,
                                    string name,
                                    string verifyingPosition)
{
    if (items== null)
    {
        Debug.Assert(false);
        throw new NullReferenceException(string.Format("{0} {1} is null.", verifyingPosition, name));
    }
    else if ( !items.Any() )
    {
        Debug.Assert(false);
        // you probably want to use a better (custom?) exception than this - EmptyEnumerableException or similar?
        throw new ApplicationException(string.Format("{0} {1} is empty.", verifyingPosition, name));

    }
}

这个可能更好 - 它展示了如何编写一个通用的扩展方法。当然,代码很奇怪 - 在这里添加文本到 errMsg 是无用的,但这不是重点。此外,我注意到 Count() 可能会遍历整个集合,这可能不是想要的。我更喜欢使用 .Any() 来检查是否有任何可用项目,尽管这也可能会产生副作用。 - Kobi
我同意errMsg的观点,但我并不想改变他实际的实现方式,只需更改签名即可满足他的需求... - Dave D
我的错误 - 错误消息发送到新的异常中,因此会做一些事情。错过了那部分:P。不过仍然更喜欢两种不同类型的异常。 - Kobi

5

IEnumerable<object> 不是 IEnumerable<T> 的超类型,因此它也不是 List<T> 的超类型。参见 问题2575363 以了解为什么会出现这种情况的简要概述(虽然它是关于Java的,但概念是相同的)。顺便说一下,这个问题已经在C# 4.0中得到解决,支持协变泛型

你没有发现这个错误的原因是因为你使用了 x as T,而应该使用普通转换 ((T)x),请参见问题2139798。由此产生的InvalidCastException将指出您的错误。(实际上,如果类型关系正确(即,如果IEnumerable<object>List<T>的超类型),则根本不需要转换。)
要解决问题,请使您的方法成为通用方法,以便它接受IEnumerable<T>而不是IEnumerable<object>,并完全跳过转换。
 public static void VerifyNotNullOrEmpty<T>(IEnumerable<T> theIEnumerable,
                                            string theIEnumerableName,
                                            string theVerifyingPosition) { ... }

在这种情况下,你不能将(IEnumerable<object>)myList进行强制类型转换。 - Kobi
@Kobi:是的,这正是我的观点:如果你使用(IEnumerable<object>)myList,你会得到一个InvalidCastException,并立即知道你正在使用错误的类型。如果你使用myList as IEnumerable<object>,你就会想知道为什么结果是空的(“myList是空的吗?还是转换失败了?”)。 - Heinzi
好的。我误会了你;你似乎在暗示这里可能需要使用强制类型转换。 - Kobi
@Kobi:没问题,感谢反馈。我已经更新了我的答案以澄清这一点。 - Heinzi

4

假设你的目标至少是3.0框架:

使用扩展将其转换为通用的IEnumerable<object>

var myEnumerable = myList.Cast<object>();

编辑: 无论如何,我建议您更改获取纯IEnumerable的方法,例如:

public static void VerifyNotNullOrEmpty(IEnumerable theIEnumerable,
                                        string theIEnumerableName,
                                        string theVerifyingPosition)

在方法内部,使用foreach或theIEnumerable.Cast<object>().Count()检查是否为空。

这样,您就不必每次都强制转换为IEnumerable<object>了。


不起作用 :-( 调用者现在调用: Validator.VerifyNotNullOrEmpty( myList.Cast<object>(), ... 被调用者仍将IEnumerable<object>作为null接收 - Avi
你确定myList不是null吗? - digEmAll

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接