当我尝试这个时:
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; }
}
答案 0 :(得分:6)
答案很简单。
在第一个版本中,您在.FirstOrDefault()
之后接听电话。如果没有元素,FirstOrDefault()
会返回null
(参见msdn)的对象。然后你尝试在空指针上调用.Value
。
在第二个版本中它是最后一个调用,在此之前Select()
将可枚举变为int-enumerable,int
的默认值为0
。
答案 1 :(得分:3)
FirstOrDefault将枚举结果并在没有符合条件的元素时返回'null',因为引用类型的默认值(T)为null。然后,当您在结果上调用.Value时,您会得到NullReferenceException
。
另一方面,通过使用Select,您还没有枚举序列,因此它只会获得与查询其余部分匹配的值,因此这就是您获得正确行为的原因(即只考虑与where子句匹配的值。)
答案 2 :(得分:1)
第一行说:“从上个月拿第一个i,然后取其值。”
问题在于,如果上个月没有i,那么你就不能其值,因为它不存在。
第二行说“从上个月取i的第一个值,如果没有,则取默认值(0)”。
这没有那个问题。
有关相同答案的更多技术版本,请参阅其他答案。
答案 3 :(得分: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”,所以不会抛出任何异常。
虽然看起来可能相同,但这两个例子根本不同。