空数组和IEnumerable <t> </t>

时间:2011-09-21 08:33:12

标签: c# arrays null ienumerable null-coalescing-operator

A previous question讨论了IEnumerable以及使用空集合而不是空值集合的约定。这是一个很好的做法,因为它消除了许多容易出错的空检查。

但答案并未完全解决其中一个案例。很多时候我强制处理空值,特别是当从外部方法返回数组时。一个例子:

(foreignObj.GetPeople() ?? Enumerable.Empty<Person>())
  .Where(p => p.Name != "John")
  .OrderBy(p => p.Name)
  .Take(4);

我编写了一个辅助方法,它确实提高了可读性。

public class SafeEnumerable
{
    public static IEnumerable<T> From<T>(T[] arr)
    {
        return arr ?? Enumerable.Empty<T>();
    }
}

导致:

SafeEnumerable.From(foreignObj.GetPeople())
  .Where(p => p.Name != "John")
  .OrderBy(p => p.Name)
  .Take(4);

我不介意,但我正在寻找更好的想法。好像我正在添加应该存在的东西。

4 个答案:

答案 0 :(得分:2)

问题找到了收集的位置(IEnumerable<T>)。如果您总是忙于检查集合的null值,则应考虑修改源。例如:

public User GetUser(long id) { }
public List<User> GetUsers(long companyId) { }

如果在找不到用户时返回null,则第一种方法有意义,null返回值表示未找到。但在我看来,第二种方法在任何正常情况下都不应该返回null。如果找不到用户,则应返回空列表,而不是null值,这意味着程序的某些内容不正确。在您的问题中给出的示例中,如果找不到directoryInfo.GetFiles("*.txt")文件,我不相信txt会返回null,而是应该返回一个空集合。

答案 1 :(得分:2)

我为IEnumerable创建了一系列扩展方法,第一个是EmptyIfNull

例如

public static class EnumerableExtensions {

  public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> collection) {
    return collection ?? Enumerable.Empty<T>();
  }

}

这允许我做

var q = foreignObj.GetPeople().EmptyIfNull().Where(p=>p.Name != "John").OrderBy(p => p.Name).Take(4);   

然后我添加了“安全”扩展程序,以便我可以缩短代码输入/更容易阅读

e.g。

 public static IEnumberable<T> SafeWhere<T>(this collection<T> source,Func<T,bool> predicate) {
   return source==null ? Enumerable.Empty<T>() : source.Where(predicate);
 }

var q = foreignObj.GetPeople().SafeWhere(p=>p.Name != "John").OrderBy(p => p.Name).Take(4);   

答案 2 :(得分:1)

如果您无法修改源以更正返回null的方法,那么您的方法是有意义的。

你也许可以把它变成一个扩展方法,这样它就可以用一种更惯用的LINQy方式:

var query = foreignObj.GetPeople()
                      .AsNonNullEnumerable()
                      .Where(p => p.Name != "John")
                      .OrderBy(p => p.Name)
                      .Take(4);

// ...

public static class EnumerableExtensions
{
    public static IEnumerable<T> AsNonNullEnumerable<T>(this IEnumerable<T> source)
    {
        return source ?? Enumerable.Empty<T>();
    }
}

答案 3 :(得分:-1)

不幸的是,我认为没有内置的东西。除非你重复一遍:

foreach(var item in (GetUsers() ?? new User[0])) // ...

稍微“更好”的实现(以C#编译器为yield return sytnax生成的内容为例),略微减少浪费内存:

class SafeEnumerable<T> : IEnumerable<T>, IEnumerator<T>
{
    private IEnumerable<T> _enumerable;
    public SafeEnumerable(IEnumerable<T> enumerable) { _enumerable = enumerable; }

    public IEnumerator<T> GetEnumerator()
    {
        if (_enumerable == null)
            return this;
        else
            return _enumerable.GetEnumerator();
    }
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }

    public T Current { get { throw new InvalidOperationException(); } }
    object System.Collections.IEnumerator.Current { get { throw new InvalidOperationException(); } }

    public bool MoveNext() { return false; }
    public void Reset() { }
    public void Dispose() { }
}