C# 在单个LINQ表达式中嵌入null测试

C# 在单个LINQ表达式中嵌入null测试,c#,linq,null,nullable,C#,Linq,Null,Nullable,让我们从一个简单的示例类开始: public class Foo { public DateTime Date { get; set; } public decimal Price { get; set; } } 然后创建一个列表: List<Foo> foos = new List<Foo>; 我想将以上两行合并如下: string s = foos.FirstOrDefault(f => f.Date == DateTime.Today).P

让我们从一个简单的示例类开始:

public class Foo
{
    public DateTime Date { get; set; }
    public decimal Price { get; set; }
}
然后创建一个列表:

List<Foo> foos = new List<Foo>;
我想将以上两行合并如下:

string s = foos.FirstOrDefault(f => f.Date == DateTime.Today).Price.ToString("0.00") ?? "N/A";
但是,这并没有达到我想要的效果,因为如果
(f=>f.Date==DateTime.Today)
没有返回Foo,那么就会抛出
NullReferenceException


因此,使用LINQ是否可以只创建一条语句来返回格式化的价格或“N/A”一种方法是在调用
ToString
之前简单地检查
FirstOrDefault
的结果是否为空:

var todayFoo = foos.FirstOrDefault(f => f.Date == DateTime.Today);
var s = todayFoo != null ? todayFoo.Price.ToString("0.00") : "N/A";
另一种方法是为合并操作符创建扩展方法,该操作符也接受投影委托,类似于:

public static class ObjectExt
{
    public static T2 Coalesce<T1, T2>(
         this T1 obj, Func<T1, T2> projection, T2 defaultValue)
    {
        if (obj == null)
            return defaultValue;

        return projection(obj);
    }
}

string s=foos.Where(f=>f.Date==DateTime.Today)。选择(f=>f.Price.ToString(“0.00”).FirstOrDefault()

如果先筛选然后选择,则可以使用空合并运算符(
??
),如下所示:


谢谢-我真的很喜欢扩展方法,但是,我只是觉得Allon的解决方案更简洁,对我来说更重要的是(虽然这不是一个要求),它对其他场景更通用。@Barry:我同意,Allon的解决方案更简单。LINQ之所以允许这样做是因为它的延迟执行,但您可以在其他情况下使用扩展方法(在访问属性之前需要合并的任何时候)。@Groo:实际上,这不是因为延迟求值-您可以在每次方法调用之后放置
.ToArray()
,以防止延迟求值,它仍然可以工作。这是由于操作的基于集合的性质,其中空集仍然是一个集合,因此对它的投影不会引发
NullReferenceException
。然而,您的扩展方法非常有用。
public static class ObjectExt
{
    public static T2 Coalesce<T1, T2>(
         this T1 obj, Func<T1, T2> projection, T2 defaultValue)
    {
        if (obj == null)
            return defaultValue;

        return projection(obj);
    }
}
var s = foos
         .FirstOrDefault(f => f.Date == DateTime.Today)
         .Coalesce(t => t.Price.ToString("0.00"), "N/A");
string price = foos.Where(f => f.Date == DateTime.Today)
                   .Select(f => f.Price.ToString())
                   .FirstOrDefault() ?? "N/A";