[C#.NET][LINQ] 在 LINQ 中,避免多餘的查詢次數的方法
延用上一篇的類別 [C#.NET] 若要自訂義基底集合類別,應避免繼承List<T>,並且增加 IterateNumber 屬性,修改GetEnumerator方法,這是用來觀察LINQ查詢語法的查詢(Iteration)次數用,當使用LINQ查詢一次IterateNumber 就會累加一次。
public class CellPhones : IEnumerable<CellPhone>, ICollection<CellPhone>
{
List<CellPhone> _items = new List<CellPhone>();
public int IterateNumber { get; set; }
public int Count
{
get { return this._items.Count; }
}
public bool IsReadOnly
{
get
{
return false;
}
}
public void Add(CellPhone Item)
{
this._items.Add(Item);
}
public bool Remove(CellPhone Item)
{
return this._items.Remove(Item);
}
public void Clear()
{
this._items.Clear();
}
public bool Contains(CellPhone item)
{
return this._items.Contains(item);
}
public void CopyTo(CellPhone[] array, int arrayIndex)
{
this._items.CopyTo(array, arrayIndex);
}
public IEnumerator<CellPhone> GetEnumerator()
{
foreach (var item in this._items)
{
this.IterateNumber++;
yield return item;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
我先為集合添加幾筆資料
CellPhones phones = new CellPhones()
{
new CellPhone(){CountryCode="+86",Phone="338-2817"},
new CellPhone(){CountryCode="+86",Phone="338-2816"},
new CellPhone(){CountryCode="+866",Phone="338-1982"},
new CellPhone(){CountryCode="+87",Phone="776-1232"},
};
接下來我們常用的LINQ語法可能是這樣,猜猜看以下兩個寫法哪個效率高?
法一:
var query = from data in phones
where data.CountryCode == "+86"
select data;
foreach (var item in query)
{
Console.WriteLine(item.CountryCode);
}
法二:
var query = from data in phones
where data.CountryCode == "+86"
select data;
var actual = query.First();
有人可能會覺得法一比較好,或許有人也覺得這兩個寫法沒什麼差。
看看接下來的測試:法一測試,IterateNumber 累加次數為4
public void QueryToListTest()
{
CellPhones phones = new CellPhones()
{
new CellPhone(){CountryCode="+86",Phone="338-2817"},
new CellPhone(){CountryCode="+86",Phone="338-2816"},
new CellPhone(){CountryCode="+866",Phone="338-1982"},
new CellPhone(){CountryCode="+87",Phone="776-1232"},
};
var query = from data in phones
where data.CountryCode == "+86"
select data;
foreach (var item in query)
{
Console.WriteLine(item.CountryCode);
}
Assert.IsTrue(phones.IterateNumber >= 4);
}
法二測試,IterateNumber 累加次數為1
public void QueryFirstTest()
{
CellPhones phones = new CellPhones()
{
new CellPhone(){CountryCode="+86",Phone="338-2817"},
new CellPhone(){CountryCode="+86",Phone="338-2816"},
new CellPhone(){CountryCode="+866",Phone="338-1982"},
new CellPhone(){CountryCode="+87",Phone="776-1232"},
};
var query = from data in phones
where data.CountryCode == "+86"
select data;
var actual = query.First();
Assert.IsTrue(phones.IterateNumber == 1);
}
這真是令人吃驚,這表示 First 方法會在條件滿足後立即返回結果,所以兩個查詢方法所得到的 IterateNumber 不一樣,這對於大集合的搜尋會帶來很大的效能幫助。
Take 跟 First 有異取同工之妙的地方,同樣都是在條件滿足後直接返回查詢,不會有多餘的Iteration,我用Take來返回滿足where條件的前兩筆資料
public void QueryTakeTest()
{
CellPhones phones = new CellPhones()
{
new CellPhone(){CountryCode="+86",Phone="338-2817"},
new CellPhone(){CountryCode="+86",Phone="338-2816"},
new CellPhone(){CountryCode="+866",Phone="338-1982"},
new CellPhone(){CountryCode="+87",Phone="776-1232"},
};
var query = from data in phones
where data.CountryCode == "+86"
select data;
var take = query.Take(2);
foreach (var item in take)
{
Console.WriteLine(item.Phone);
}
Assert.IsTrue(phones.IterateNumber == 2);
}
FirstOrDefault 比 First 更好!
接下來看看我以前會用的蠢方法(拍腦):當時為了滿足where條件,不讓First拋出例外(Note1),先調用Count()方法,再調用First()
public void QueryFirstTest1()
{
CellPhones phones = new CellPhones()
{
new CellPhone(){CountryCode="+86",Phone="338-2817"},
new CellPhone(){CountryCode="+86",Phone="338-2816"},
new CellPhone(){CountryCode="+866",Phone="338-1982"},
new CellPhone(){CountryCode="+87",Phone="776-1232"},
};
var query = from data in phones
where data.CountryCode == "+86"
select data;
int count = query.Count();
if (count >= 1)
{
CellPhone actual = (CellPhone)query.First();
}
Assert.IsTrue(phones.IterateNumber == 5);
}
這樣的寫法會IterateNumber 為5,因為Count方法跑完之後IterateNumber =4,再加上First方法,總共5次改用FirstOrDefault後,再判斷是否為null,就不會有問題了
public void QueryFirstTest1()
{
CellPhones phones = new CellPhones()
{
new CellPhone(){CountryCode="+86",Phone="338-2817"},
new CellPhone(){CountryCode="+86",Phone="338-2816"},
new CellPhone(){CountryCode="+866",Phone="338-1982"},
new CellPhone(){CountryCode="+87",Phone="776-1232"},
};
var query = (from data in phones
where data.CountryCode == "+8611"
select data).FirstOrDefault();
if (query != null)
{
//TODO:做自己要做的事
}
Assert.IsTrue(phones.IterateNumber == 4);
}
Note1.當where條件無法滿足時,調用First方法後拋出例外。
var query = (from data in phones
where data.CountryCode == "+8611"
select data).First();
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET