0

当我尝试这个时:

Month1Value = 
    g.Where(i => DateTime.ParseExact(i.Period, fmt, cult).Month == lastMonth)
    .FirstOrDefault()
    .Value

如果上个月有数据,我得到Value分配就好了。如果没有价值,我会得到这个异常:

你调用的对象是空的

但是,如果我将以上内容更改为:

Month1Value = 
     g.Where(i => DateTime.ParseExact(i.Period, fmt, cult).Month == lastMonth)
     .Select(x=>x.Value)
     .FirstOrDefault()

如果没有匹配的元素,我会得到一个零,就像我想要的那样。

这两种编写 lambda 表达式的方式有什么区别?

SSCCE:

void Main() {
    var currentMonth = DateTime.Now.Month;
    var currentTimeStamp = DateTime.Now;

    int lastMonth = currentTimeStamp.AddMonths(-1).Month; // 9

    System.Globalization.CultureInfo cInfo = new System.Globalization.CultureInfo("en-US");
    System.Globalization.DateTimeFormatInfo english = cInfo.DateTimeFormat;
    var cult = System.Globalization.CultureInfo.InvariantCulture;
    string fmt = "yyyyMM";

    var DataResults = new[] {
        new Data() {Desc="Item Name", Seq=10639, Period="200906", Value=1.65M},
        new Data() {Desc="Item Name", Seq=10639, Period="200907", Value=1.56M},
        new Data() {Desc="Item Name", Seq=10639, Period="200905", Value=1.62M},
        new Data() {Desc="Item Name", Seq=10639, Period="200908", Value=1.6M}
    };

    var pivotedResults =
        DataResults
        .GroupBy(i => new { i.Desc, i.Seq })
        .Select((g, k) => new PivotedData
        {
            Desc = g.Key.Desc,
            Seq = g.Key.Seq,
            // Month1Value = g.Where(i => DateTime.ParseExact(i.Period, fmt, cult).Month == lastMonth).FirstOrDefault().Value,
            Month1Value = g.Where(i => DateTime.ParseExact(i.Period, fmt, cult).Month == lastMonth).Select(x => x.Value).FirstOrDefault(),
            Month1 = english.GetAbbreviatedMonthName(lastMonth),
        }).ToList();    

    DataResults.Dump();
    pivotedResults.Dump();
}

public class Data {
   public string Desc { get; set; }
   public long Seq { get; set; }
   public string Period { get; set; }
   public decimal Value { get; set; }
}

public class PivotedData {
   public string Desc { get; set; }
   public long Seq { get; set; }
   public string Month1 { get; set; }
   public decimal? Month1Value { get; set; }
}
4

4 回答 4

6

答案很简单。

在第一个版本中,您在.FirstOrDefault(). 如果没有元素,则FirstOrDefault()返回(参见 msdn)对象。null然后你尝试调用.Value一个空指针。

在第二个版本中,它是最后一次调用,在此之前Select()将 enumerable 转换为 int-enumerable 并且默认为intis 0

于 2013-10-31T14:33:34.553 回答
3

FirstOrDefault将枚举结果并在没有匹配条件的元素时返回“null”,因为引用类型的 default(T) 为 null。然后,当您在结果上调用 .Value 时,您会得到一个 .Value NullReferenceException

另一方面,通过使用Select您还没有枚举序列,因此它将仅获得与查询的其余部分匹配的值,这就是您获得正确行为的原因(即仅与where 子句被考虑)。

于 2013-10-31T14:33:45.790 回答
1
Month1Value = 
    g.Where(i => DateTime.ParseExact(i.Period, fmt, cult).Month == lastMonth)
    .FirstOrDefault()
    .Value

尝试访问可以为空的“.FirstOrDefault()”的结果。因此,如果它是 null,它会抛出 null ref。

Month1Value = 
     g.Where(i => DateTime.ParseExact(i.Period, fmt, cult).Month == lastMonth)
     .Select(x=>x.Value)
     .FirstOrDefault()

首先选择值,然后获取第一个。结果仍然可能为 null,但因为您没有在其上调用“.Value”,所以不会引发异常。

尽管它们可能看起来相同,但这两个示例根本不同。

于 2013-10-31T14:38:20.307 回答
1

第一行说:“取上个月的第一个 i,然后取它的值。”

这样做的问题是,如果上个月没有 i ,那么你就不能取它的值,因为不存在。

第二行说“取上个月 i 的第一个值,如果没有,则取默认值 (0)”。

这没有那个问题。

有关同一答案的更多技术版本,请参阅其他答案。

于 2013-10-31T14:36:36.133 回答