LINQ:当对象可能为null时,选择属性的更好方法是什么?

时间:2014-04-09 11:12:34

标签: c# linq

var result = foo.FirstOrDefault(f => f.bar == barVal).someProperty

如果没有匹配(默认为null),则无效 - 尝试访问空对象上的属性。 我们可以改写如下:

var result = foo.Where(f => f.bar == barVal)
                .Select(f => f.someProperty).DefaultIfEmpty(0).First()

虽然它有效,但这似乎不是最优雅的方式...... 有更好的方法吗?


当然可以做一些事情,例如:

var result = 0;
var tmp = foo.FirstOrDefault(f => f.bar == barVal);
if(tmp != null) result = tmp.someProperty

但是在一个更复杂的查询中,这种方法看起来甚至比DefaultIfEmpty方法更“杂乱”

var tmpSet = dataSet.GroupBy(f => f.ID);
var newSet = tmp.Select(f => new { 
               ID = f.ID,
               SomeProperty = f.Where(g => g.bar == barVal)
                               .Select(f => f.SomeProperty)
                               .DefaultIfEmpty(0).First()
               });

3 个答案:

答案 0 :(得分:3)

你可以这样做:

var result = foo.Where(f => f.bar == barVal)
                .Select(f => f.someProperty)
                .FirstOrDefault();

或者您可以编写自定义扩展方法:

public static TResult IfNotNull<TSource, TResult>(this TSource instance, Func<TSource, TResult> getter, TResult defaultValue = default(TResult))
    where TSource : class
{
     if (instance != null)
          return getter(instance);
     return defaultValue;
}

...

var result = foo.FirstOrDefault(f => f.bar == barVal)
                .IfNotNull(f => f.someProperty);

编辑:使用C#6,您将能够写下这个:

var result = foo.FirstOrDefault(f => f.bar == barVal)?.someProperty ?? 0;

有关详细信息,请参阅Roslyn Codeplex网站上的this discussion

答案 1 :(得分:1)

我前一段时间问a similar question;我发现的最好方法是使用来自FirstOrDefault的默认值或使用DefaultIfEmpty作为非默认值。只需首先在Select Linq查询中取消引用该属性

所以我真的没有看到任何更好的解除财产的方法。扩展方法是将这种行为折叠成更具表现力的名称的唯一方法。

答案 2 :(得分:0)

现在最有意识的解决方案似乎是一种扩展方法。

var foo = "foo";
var fooNull = (String)null;

var fooLength = foo.Get(_ => _.Length, -1);         //  3
var fooNullLength = fooNull.Get(_ => _.Length, -1); // -1

var bar = new[] { "bar" };
var barNull = new[] { (String)null };

var barLength = bar.GetSingle(_ => _.Length, -1);         //  3
var barNullLength = barNull.GetSingle(_ => _.Length, -1); // -1

var baz = new[] { null, "bar", null };

var bazLength = baz.Get(_ => _.Length, -1); // { -1, 3, -1 }

我将按如下方式实现此操作,因为简洁性而忽略了错误处理。请注意,我在这里使用了比上例更有意义但更长的方法名称。

// Get the value of a property of an object or a default
// value if the object is null.
public static TProperty GetGetPropertyValueOrDefault<TObject, TProperty>(this TObject obj, Func<TObject, TProperty> propertyValueGetter, TProperty defaultValue = default(TProperty))
   where TObject : class
{
   return (obj != null) ? propertyValueGetter(obj) : defaultValue;
}

// Get the value of a property for the single object in a
// sequence or a default value if the sequence is empty.
public static TProperty GetSinglePropertyValueOrDefault<TObject, TProperty>(this IEnumerable<TObject> sequence, Func<TObject, TProperty> propertyValueGetter, TProperty defaultValue = default(TProperty))
   where TObject : class
{
    return sequence.SingleOrDefault().GetGetPropertyValueOrDefault(propertyValueGetter, defaultValue);
}

// Get the value of a property or a default value if the
// object is null for all objects in a sequence.
public static IEnumerable<TProperty> GetGetPropertyValuesOrDefault<TObject, TProperty>(this IEnumerable<TObject> sequence, Func<TObject, TProperty> propertyValueGetter, TProperty defaultValue = default(TProperty))
   where TObject : class
{
    return sequence.Select(element => element.GetGetPropertyValueOrDefault(propertyValueGetter, defaultValue));
}