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



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

public class Foo
{
    public DateTime Date { get; set; }
    public decimal Price { get; set; }
}

然后创建一个列表:

List<Foo> foos = new List<Foo>;

我想根据日期返回列表中一个项目的格式化价格或"N/a",因此,例如,我可以写:

Foo foo = foos.FirstOrDefault(f => f.Date == DateTime.Today);
string s = (foo != null) ? foo.Price.ToString("0.00") : "N/A";

我想把上面的两行这样组合如下:

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"?

如果您先过滤然后选择,您可以使用空合并操作符(??),如下所示:

string price = foos.Where(f => f.Date == DateTime.Today)
                   .Select(f => f.Price.ToString())
                   .FirstOrDefault() ?? "N/A";

一种方法是在调用ToString之前简单地检查FirstOrDefault的结果是否为null:

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);
    }
}

然后像这样调用它:

var s = foos
         .FirstOrDefault(f => f.Date == DateTime.Today)
         .Coalesce(t => t.Price.ToString("0.00"), "N/A");

string s = foos.Where(f => f.Date == DateTime.Today).Select(f => f.Price.ToString("0.00")).FirstOrDefault();

最新更新