C# 中奇妙的函数 -- 4. Empty, DefaultIfEmpty, Count

时间:2021-12-17 17:18:31

Empty 静态方法

有多少次你不得不从一个方法返回一个空集合(无论是由于错误的条件,或者没有项目存在),并创建了一个空数组或列表?

让我们看一个简单的POCO 类,它包含了两个银行帐户转帐的信息:

   public class Transfer
   {
        public string FromAccount { get; set; }
        public string ToAccount { get; set; }
        public double Amount { get; set; }
   }
 

现在,比方说我们有一个数据访问对象Data access object,它是用于收集一些特别的转帐并且返回他们。 但是,如果当转帐服务正在维护中,我们就返回一个空序列。

我们当然可以返回 空值 NULL ,但返回空序列一般比空值NULL好。 因此,我们这样做:

public class TransferDao

{

    public IEnumerable<Transfer> GetPendingTransfers()

    {

        if (!_txfrService.IsInMaintenanceMode())

        {

            // 创建一个列表,并返回它...

        }

        // 否则,因为我们是在维护模式,返回一个空的集合:

        return new List<Transfer>(0);

    }

}

这里的问题是,我们基本上是浪费了内存分配。 如果我们打算做的是返回给定类型的只读空序列,我们可以使用LINQ的空序列来代表它 -- 尽量不要浪费内存分配:

public class TransferDao

{

    public IEnumerable<Transfer> GetPendingTransfers()

    {

        if (!_txfrService.IsInMaintenanceMode())

        {

            // 创建一个列表,并返回它...

        }

        // 否则,因为我们是在维护模式,返回一个空的集合:

        return Enumerable.Empty<Transfer>();

    }

}

请注意,这次我们调用 了  Enumerable 的 Empty<T>() 这个静态方法,Enumerable 扩展定义了接口 IEnumerable的 方法,Empty 其实只是一个简单的静态方法,它返回类型为 T 的空序列。

DefaultIfEmpty 扩展方法

我们已经会用Empty生成空序列。 但问题是,如果你想返回一个序列当序列是空的时候包含一个默认的项目怎么办?

比如说,如果你正在分析测试成绩列表,如果学生没有得分,要返回一个 0:

var scores = new[] { 73, 77, 89, 90, 92, 77 };

foreach (var score in scores.DefaultIfEmpty())

{

    Console.WriteLine("The score is: " + score);

}

 

List<int> emptyScores = new List<int>();

foreach (var score in emptyScores.DefaultIfEmpty())
{
    Console.WriteLine("The score is: " + score);

}

现在,还有第二种形式的DefaultIfEmpty(),可让您指定要使用的默认值,而不是依赖于 Default(T) 。 例如,如果我们希望得到平均分,但如果是空值就要返回一个100.

  var averageSoFar = scores.DefaultIfEmpty(100).Average();

Count() 扩展方法

这个方法的作用看起来是显而易见的,对不对? 在大多数情况下是的。它也有一些不错的功能,值得一提。

首先,调用 不带参数的 Count() 返回序列计数。 这可能看起来对于List<T>有点像多余的:

var scoreArray = new[] { 73, 77, 89, 90, 92, 77 };

var scoreList = new List<int> { 73, 77, 89, 90, 92, 77 };

Console.WriteLine(scoreArray.Length);

Console.WriteLine(scoreArray.Count());

Console.WriteLine(scoreList.Count);

Console.WriteLine(scoreList.Count());

单单的一个 count() , 不是很令人兴奋啊。 那么, 看看它的重载 predicate ( Func<T, bool> ),让你数算符合条件的所有项目:

var scoreList = new List<int> { 73, 77, 89, 90, 92, 77 };
var numberOfBsOrBetter = scoreList.Count(s => s >= 80);

 

//为什么要用这个呢?既然我们有那个方便的重载

var numberOfBsOrBetter = scoreList.Where(s => s >= 80).Count();

Count() VS Any()

该 Count() 扩展方法用于检查项目的数量,但如果你想知道的是一个序列是不是空,可能更有效的是使用Any() 扩展方法。

    / /这个遍历所有项目,即使第一项已经合乎要求了 
var hasAnyBsOrBetter = scoreList.Count(s => s >= 80) != 0;
 
   / /它将在第一项比较之后就停止,而不进一步检查
var hasAnyBsOrBetter2 = scoreList.Any(s => s >= 80);
 

请注意, Any(), 就像 COUNT()一样,也有不带参数的形式:

var isEmpty = someSequence.Count() == 0;
var isEmpty2 = !someSequence.Any();
 

如果序列是类似一个数组列表,可能没有关系,因为他们需要一个固定的时间 -- 为O(1) -- 来计数,但对于其他序列,使用Any()更有效的检查是否非空。

 转载:喜乐的ASP.NET(Alex Song)