在Linq的Where条件中,如何优雅的进行类型转换?
大家应该都知道Int32.Parse()是不安全,但有时可能会有侥幸心理,而我正是在这样的心理驱使下,这么干了。相关场景简化处理后的代码,如下。
List<BookInfo> bookLst = new List<BookInfo>(); for (var i = 0; i < 100000000; i++) { bookLst.Add(new BookInfo() { Num = i.ToString() }); } //原有写法 var finalBookLst1= bookLst.Where(p => Int32.Parse(p.Num) > 10).ToList(); public class BookInfo { public string Num { get; set; } }
很显然,在linq的where条件之中,直接进行类型转换是不安全的,基于此我们需要对这段代码进行优化。
既然是对集合进行操作,那么我们可以使用for循环,来替代原有的where条件,因此便有了下面一段代码。
var finalBookLst2= new List<BookInfo>(); for (var j = 0; j < bookLst.Count; j++) { int num1; Int32.TryParse(bookLst[j].Num,out num1); if (num1 > 10) finalBookLst2.Add(bookLst[j]); }
在上述代码中,我们直接遍历集合中的每一个对象,对其进行业务判断后,将满足条件的对象加入到预期结果的集合之中,最后我们便得到了最终的结果。
用for循环写完之后,我的第一想法就是这多low呀,性能得多差呀。于是乎经过了一番百度,我们的第二个方案便产生了。
ConcurrentBag<BookInfo> finalBookLst3 = new ConcurrentBag<BookInfo>(); Parallel.For(0,bookLst.Count,(num)=> { int num2; Int32.TryParse(bookLst[num].Num,out num2); if (num2 > 10) finalBookLst3.Add(bookLst[num]); });
Parallel主要用于任务的并行执行,在上面的示例中,我们将我们业务判断通过Parallel并行执行,希望能够优化响应时间。但是,悲催的是,使用Parallel的方案更加耗时。
从以上示例可以看出,并行执行并不一定比串行执行效率高,因为并行执行是有额外开销。同时需要支出的是,List是线程不安全的,因此我们使用ConcurrentBag<T>来保存处理结果。
而对于bookLst的访问,由于不存在资源竞争,所以是安全。
木得办法,由于Parallel性能太差,我们是要舍弃这种方案的,想要替代for循环还得另寻它法,最后便有了如下代码的产生,这也是我们最终选择的方案。
private static bool CheckNum(BookInfo bookInfo) { int num; Int32.TryParse(bookInfo.Num, out num); if (num > 10) return true; else return false; } var finalBookLst4 = bookLst.Where(new Func<BookInfo,bool>(CheckNum)).ToList();