首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >懒人的IEnumerable可拓验证方法

懒人的IEnumerable可拓验证方法
EN

Code Review用户
提问于 2014-04-21 17:18:08
回答 3查看 2.8K关注 0票数 5

我经常发现自己用列表和其他集合类型来做这个(或类似的)工作:

代码语言:javascript
复制
if (someList != null && someList.Count > 0)
    //take some action on the list

因此,我变得懒惰,并编写了以下扩展方法来帮助我做到这一点:

代码语言:javascript
复制
    public static bool IsNullOrEmpty(this IEnumerable self)
    {   
        //Convert to collection here because IEnumerable doesn't have a count property
        ICollection c = self as ICollection; 
        return c == null || c.Count < 1;
    }

    public static bool IsNotNullOrEmpty(this IEnumerable self)
    {
        return !self.IsNullOrEmpty();
    }

    public static bool IsNotNullAndContains<T>(this IEnumerable<T> self, T item)
    {
        return self.IsNotNullOrEmpty() && self.Contains(item);
    }

除了懒惰之外,这样做有什么问题吗?还是每次我都应该坚持手动检查?

编辑:

根据Jeroen Vannevel的答复:

我最初将第一个扩展方法(IsNullOrEmpty)定义为:

代码语言:javascript
复制
public static bool IsNullOrEmpty<T>(this IEnumerable<T> self)
{
    return self == null || self.Count < 1;
}

它消除了对ICollection的强制转换的需要,因为IEnumerable<T>实现了count属性。但是,我删除了类型参数,因为我实际上并没有将它用于任何事情。我是否应该保持这样的状态,以防止抛出和潜在类型的抛出异常?

EN

回答 3

Code Review用户

回答已采纳

发布于 2014-04-21 17:37:49

它看上去很方便,但我有一些想法。

首先:当您创建自己的IEnumerable<T>而不实现ICollection时,您将得到一个逻辑错误。它将尝试使用as来转换它,这将返回null,因为不支持转换。现在,返回语句c == null将返回true,因此实现IEnumerable<T>而不是ICollection的任何集合都将被视为空或空。

我不知道.NET中是否也有这样的集合,但是您可能也想检查一下。

其次:前两个方法执行一个非常密切相关的操作,因此可以将这些操作合并到一个方法中。第三,做两件事并不真正相似,而且“一种方法应该做一件事”的指导思想被违反了。

一般情况下:一旦您将And添加到您的方法名称中,它就应该是一个危险的标志。

最后,一个空集合(又名:Count = 0)不是迭代的问题。迭代器很快就会从代码块中分离出来,所以通过检查Count > 0没有提高性能。这里有更多阅读材料

票数 6
EN

Code Review用户

发布于 2014-04-21 18:34:28

在我看来,这种方法一开始就不需要了。我认为很少会涉及到传递null IEnumerable (或List<T> )的情况。IEnumerable仍然可以表示缺少可迭代的内容,因此您不会经常看到IEnumerable成员返回null。当更好的选项是失败或更明确地通知调用方无效操作时,我经常会看到null返回IEnumerable。以这个Repository为例:

代码语言:javascript
复制
public class Repository<T>
{
    public IEnumerable<T> GetItems()
    {
        bool canConnectToDb = false;
        bool recordsReturned = false;

        // Case 1:
        if(!canConnectToDb)
            return Enumerable.Empty<T>();

        // Case 2:
        if(!canConnectToDb)
            return null;

        // Case 3:
        if(!recordsReturned)
            return null;
    }
}

在“案例1”中,结果具有误导性。当然,不能返回任何记录,但原因并不是缺少需要返回的记录。相反,必须处理错误大小写并引发异常。在"Case 2“中,null用于显示错误状态,这是误导性的。是什么错误?如何区分空响应和使用try..catch的错误处理代码?在“案例3”中,正确的返回值将是空的IEnumerable,而不是null

这就引出了您的扩展methods...Usually检查,像这样的检查试图掩盖问题,而不是早期失败并识别它们。例如,让我们应用扩展方法重构现有的Count()扩展方法:

代码语言:javascript
复制
public static int Count<T>(this IEnumerable<T> iterator)
{
    if(iterator.IsNullOrEmpty())
        return 0;
    else
        // Iterate and count...
}

您只需执行这个问题就可以创建真正隐藏问题的“安全”代码( iterator本来不应该作为null传入)。实际的实现可能更像是:

代码语言:javascript
复制
public static int Count<T>(this IEnumerable<T> iterator)
{
    if(iterator == null)
        throw new ArgumentException("iterator");

    // Iterate and count...
}

这允许它在使用不当的情况下尽早失败,而且这种方法没有责任确保它总是在恶劣的情况下执行。

最后,如果您确实计划使用类似于您的扩展方法,那么您应该知道,.NET框架附带的扩展方法已经进行了与检查对象是否为ICollection类似的性能检查。尽管如此,对于实现IEnumerable的自定义类型,没有任何性能检查,所以如果您想要计数,唯一的选择就是迭代整个IEnumerable。不过,您并不真正需要计数,您只需要知道是否返回了什么,Any()扩展方法就是这样做的。它不会迭代整个集合,它只会迭代一次,如果有什么,它会立即返回true,这可能是一个巨大的性能保护程序。

重做你的方法:

代码语言:javascript
复制
public static bool IsNullOrEmpty(this IEnumerable iterator)
{
    return iterator == null || !iterator.Any();
}

也许我可以通过看到这个扩展方法的一个很好的应用来说服我,但是我想不出其中的一个。

编辑-为了解决使用另一个您不能完全信任的库的问题,您有一些其他选项:

1)使用立面图案创建符合标准的包装器对象。

2)将调用站点上的问题处理给这些外部库,而不是深入到您自己的代码中。例如:

代码语言:javascript
复制
var items = BadLibrary.GetItems<T>() ?? Enumerable.Empty<T>();
InternalGoodMethod(items);

public static void InternalGoodMethod(IEnumerable<T> items)
{
    if(items == null)
        throw new ArgumentException("items");

    ....
票数 7
EN

Code Review用户

发布于 2014-04-21 21:58:37

公共静态bool IsNullOrEmpty(此IEnumerable self) {// IEnumerable在这里转换为集合,因为IEnumerable没有计数属性ICollection c= self作为ICollection;返回c == null c.Count < 1;}

如前所述,以ICollection为例,将类型转换到Enumerable.Range(0, 10);可能有问题。

公共静态bool IsNullOrEmpty(此IEnumerable self) {返回自== null \ self.Count < 1;}

编辑:--这是错误的,这要感谢注释--也许我错了,但是self == null永远不会发生,如果self == null将在对空值调用该方法时抛出一个Object reference not set to an instance of an object异常。

IEnumerable对象也可以延迟实现,因此计数实际上将迭代所有可枚举对象。我不认为这是可取的‘到处’的零或空支票。

无论如何,不同的需求需要不同的对象处理,尽管我主要同意Ocelot20的答案。

  • 当返回IEnumerable对象时,通常不应该返回空值,而应该返回Enumerable.Empty();
票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/47800

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档