检查集合中的null

时间:2013-02-07 20:52:49

标签: c# linq

哪种空检查方式更好用,为什么?

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

或:

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

或者没有区别?

6 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:1)

你想要一个元素。

因此,带有空检查的FirstOrDefault()

更清晰
Where
Any
First

为了表现,在大多数情况下,这不会改变你的生活。但我会首先考虑“可读性”。

答案 2 :(得分:1)

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

答案 3 :(得分:1)

第一个选项可能会因为null项目通过检查而中断,从而使您认为没有匹配的项目,即使它们是。它不适用于这个特定的例子,但它可以适用于一般情况。

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

如果您需要使用第一个选项担心此特定边缘情况,或者您希望获得使用AnyFirst的好处(因为它们对您的优越语义表示想要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;
}

答案 4 :(得分:0)

我倾向于使用涉及FirstOrDefault后面的Where的表达式:

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

正如Raphael Althaus上面指出的那样,你想要一个变量被空检查,在我看来你应该首先用条件进行查询,然后选择第一个如果存在,并检查。

答案 5 :(得分:0)

两个approches 不同,这里是ILs:

FirstOrDefault + 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
  • FirstOrDefault

其中 + 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的元素不多{1}}这并不重要。在这两者之间我肯定更喜欢最清晰的语法

顺便说一下,我是null的忠实粉丝......那么请问if (myVar != default(T))呢?