2

Which way of null checking would be better to use and why?

var myVar = myCollection.FirstOrDefault(q => q.Id == 10);
if (myVar != null)
{
   anotherVar = myVar.MyName;
}

or:

var myVar = myCollection.Where(q => q.Id == 10);
if (myVar.Any())
{
   anotherVar = myVar.First().MyName;
}

or just no difference?

4

6 回答 6

1

可能是过早的优化,但第一种方法只需要一次执行,因此效率更高。

当我稍后可能再次懒惰地需要查询时,我正在使用第二种方法。“FirstOrDefault完成”它。

于 2013-02-07T20:54:49.250 回答
1

你想要一个元素。

所以一个FirstOrDefault()带有空检查的比

Where
Any
First

就性能而言,在大多数情况下,这不会改变你的生活。但我会首先考虑“可读性”。

于 2013-02-07T20:54:50.603 回答
1

我更喜欢第一种方式,因为它更清楚你打算做什么。

于 2013-02-07T20:54:59.433 回答
1

第一个选项可能会由于null通过检查的项目而被破坏,从而使您认为没有匹配的项目,即使它们是。它不适用于此特定示例,但可以适用于一般情况。

但是,这里的第二个示例(有时)迭代源序列两次,一次是查看是否有任何结果,然后再次获得该结果。如果源序列需要执行数据库查询以获取可能非常昂贵的结果。因此,如果您确定您正在处理一个内存中的集合并且它不是特别大(或者您需要的第一个项目将很快找到),您应该只使用此选项。

如果您需要担心第一个选项的这种特殊边缘情况,或者您想获得使用的好处,Any并且First(因为它们对您想要的东西的优越语义表示)具有性能优势,FirstOrDefault您可以使用这种模式:

var myVar = myCollection.Where(q => q.Id == 10)
    .Take(1)
    .ToList();
if (myVar.Any())
{
   anotherVar = myVar.First().MyName;
}

如果你愿意,你可以做一个扩展方法来缩短它:

public static IEnumerable<T> FirstOrEmpty<T>(this IEnumerable<T> source)
{
    //TODO: null check arguments
    using (var iterator = source.GetEnumerator())
    {
        if (iterator.MoveNext())
            return new T[] { iterator.Current };
        else
            return Enumerable.Empty<T>();
    }
}

public static IEnumerable<T> FirstOrEmpty<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
    return FirstOrEmpty(source.Where(predicate));
}

这将允许您只写:

var myVar = myCollection.FirstOrEmpty(q => q.Id == 10);
if (myVar.Any())
{
   anotherVar = myVar.First().MyName;
}
于 2013-02-07T20:56:41.117 回答
0

这两种方法不同的,这里是 IL:

第一或默认+if (myVar != null)

IL_0067:  ldsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate1
IL_006C:  brtrue.s    IL_007F
IL_006E:  ldnull      
IL_006F:  ldftn       b__0
IL_0075:  newobj      System.Func<<>f__AnonymousType0<System.Int32,System.String>,System.Boolean>..ctor
IL_007A:  stsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate1
IL_007F:  ldsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate1
IL_0084:  call        System.Linq.Enumerable.FirstOrDefault  <-----
IL_0089:  stloc.2     // myVar
IL_008A:  ldloc.2     // myVar
IL_008B:  brfalse.s   IL_0094
IL_008D:  ldloc.2     // myVar
IL_008E:  callvirt    <>f__AnonymousType0<System.Int32,System.String>.get_MyName
IL_0093:  stloc.1     // anotherVar
IL_0094:  ldloc.1     // anotherVar
  • 第一或默认

在哪里+if (myVar.Any())

IL_0067:  ldsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate1
IL_006C:  brtrue.s    IL_007F
IL_006E:  ldnull      
IL_006F:  ldftn       b__0
IL_0075:  newobj      System.Func<<>f__AnonymousType0<System.Int32,System.String>,System.Boolean>..ctor
IL_007A:  stsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate1
IL_007F:  ldsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate1
IL_0084:  call        System.Linq.Enumerable.Where   <-----
IL_0089:  stloc.2     // myVar
IL_008A:  ldloc.2     // myVar
IL_008B:  call        System.Linq.Enumerable.Any     <-----
IL_0090:  brfalse.s   IL_009E
IL_0092:  ldloc.2     // myVar
IL_0093:  call        System.Linq.Enumerable.First   <-----
IL_0098:  callvirt    <>f__AnonymousType0<System.Int32,System.String>.get_MyName
IL_009D:  stloc.1     // anotherVar
IL_009E:  ldloc.1     // anotherVar
  • 在哪里
  • 任何
  • 第一的

它看起来像微优化,但第一个应该更快,因为单个枚举在FirstOrDefault但如果后面的枚举器Where包含的元素不多,q.Id == 10它并不重要。在两者之间,我肯定更喜欢最清晰的语法

顺便说一句,我很null喜欢……usign怎么样if (myVar != default(T))

于 2013-02-08T03:44:09.093 回答
0

I tend to use expressions that involve Where followed by FirstOrDefault:

var myVar = myCollection.Where(x => x.Id==10).FirstOrDefault();
if (myVar != null)
{
   anotherVar = myVar.MyName;
}

As Raphael Althaus points out above, you want one variable to be null checked, in my opinion you should first make a query with the condition and then pick the first one if exists, and check that.

于 2013-02-07T21:05:16.963 回答