哪种空检查方式更好用,为什么?
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;
}
或者没有区别?
答案 0 :(得分:1)
可能是过早优化,但第一种方法只需要一次执行,因此它的效率会更高一些。
我正在使用第二种方法,因为我可能会在懒惰后再次需要查询。 FirstOrDefault
“完成”它。
答案 1 :(得分:1)
你想要一个元素。
因此,带有空检查的FirstOrDefault()
比
Where
Any
First
为了表现,在大多数情况下,这不会改变你的生活。但我会首先考虑“可读性”。
答案 2 :(得分:1)
我更喜欢第一种方式,因为你打算做的更清楚。
答案 3 :(得分: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;
}
答案 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
其中 + 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))
呢?