LINQ筆記
※NoLINQ
如何改為最大可維護性
※NoLINQ與LINQ
效能差距
※迭代子
迭代子只往前走不往後走(foreach作法)
※LIKE & IN
※OrderBy&ThenBy
※First&Last
※Single
※ElementAt、ElementAtOrDefault
※取部分集合類函式
※Let
※Into
※DefaultIfEmpty
※Take & Skip
※TakeWhile & SkipWhile
※Except & Intersect
※NoLINQ
思考:
如何改為最大可維護性
第一步:
Sub Function
/// <summary>
/// Sub Function
/// </summary>
/// <param name="datasource"></param>
/// <param name="findItem"></param>
/// <returns></returns>
static int Filter(int[] datasource, int findItem)
{
foreach (var item in datasource)
{
return item;
}
return -1;
}
第二步:
泛型
/// <summary>
/// 第二步:泛型
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="datasource"></param>
/// <param name="findItem"></param>
/// <returns></returns>
static T Filter<T>(T[] datasource, T findItem)
{
foreach (var item in datasource)
{
if (item.Equals(findItem))
{
return item;
}
}
return default(T);
}
第三步:
Delegate
/// <summary>
/// 第三步:Delegate
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="datasource"></param>
/// <param name="findItem"></param>
/// <returns></returns>
static void Filter<T>(T[] datasource, T findItem, Action<T> processor)
{
foreach(var item in datasource)
{
if(item.Equals(findItem)){
processor(item);
}
}
}
第四步:
抽離判斷式
static bool Prediction<T>(T item, Func<T, bool> func)
{
return func(item);
}
第五步:
抽離迴圈
/// <summary>
/// 第五步:抽離迴圈
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="datasource"></param>
/// <param name="findItem"></param>
/// <returns></returns>
static List<T> Filter<T>(List<T> datasource, Func<T, bool> func)
{
var result = new List<T>();
foreach(var item in datasource)
{
if (func(item))
{
result.Add(item);
}
}
return result;
}
使用 設計模式:樣板方法模式(Template Method Pattern)
缺點 List<T> 占用記憶體
更改方式:使用 Enumerable 跟 Enumerator 方式可以解決這個問題
///// <summary>
///// 第六步:LIST<T>更改為IEnumerable
///// </summary>
///// <typeparam name="T"></typeparam>
///// <param name="datasource"></param>
///// <param name="findItem"></param>
///// <returns></returns>
public static IEnumerable<T> Filter<T>(T[] source, Func<T,bool> func)
{
foreach (var item in source)
{
if (func(item))
{
yield return item;
}
}
}
Extension Method
1.Static Class
2.Static Method
3.使用端必須引用該 static 類別的 namespace
public static class Filter
{
public static IEnumerable<T> FilterMember<T>(this T[] source, Func<T, bool> func)
{
foreach (var item in source)
{
if (func(item))
{
yield return item;
}
}
}
}
※NoLINQ & LINQ
NoLINQ與LINQ效能(差別在於呼叫Delegate的次數)
static void UseLinq()
{
var data = new List<int>();
Random r = new Random((int)DateTime.Now.Ticks);
for (int i = 0; i < 100000; i++)
{
data.Add(r.Next(1, 1000));
}
data.Add(99999);
Stopwatch sw = new Stopwatch();
sw.Start();
//會較慢的原因,是因為Delegate(被呼叫很多次)
var result = data.Where((s) => s == 99999).FirstOrDefault();
sw.Stop();
Console.WriteLine($"{result} {sw.ElapsedMilliseconds}");
}
印出結果:99999 5
static void UseNonLinq()
{
var data = new List<int>();
Random r = new Random((int)DateTime.Now.Ticks);
for (int i = 0; i < 100000; i++)
{
data.Add(r.Next(1, 1000));
}
data.Add(99999);
Stopwatch sw = new Stopwatch();
sw.Start();
foreach(var item in data)
{
if(item == 99999)
{
sw.Stop();
Console.WriteLine($"{item} {sw.ElapsedMilliseconds}");
break;
}
}
}
印出結果:99999 0
## string[]實作的interface
////string[]實作IEnumerable
string[] strArray = new string[] { "aaaaa", "bbbbb" };
Type[] types = strArray.GetType().GetInterfaces();
foreach (var item in types)
{
Console.WriteLine(item);
}
※迭代子
迭代子只往前走不往後走(foreach作法)
1.跑過的就可以丟掉,不佔用記憶體
2.後面可以不取
IEnumerator iterator = dataArray.GetEnumerator();
while (iterator.MoveNext())
{
Console.WriteLine(iterator.Current);
}
var dataArray = new int[] { 1, 2, 3, 5, 6, 8 };
/// 兩個WHERE但只跑一個迴圈(Base On IEnumerator)
var r = dataArray.Where(item => item > 2).Where(item => item > 9).FirstOrDefault();
LINQ Base on Enumerator
迭代子適用於未知大小清單、也適用於Delay loading(延遲載入)
在網路環境下,容易做到分批讀取(EX:分頁)
所有C#都會兩段式編譯
(C# 撰寫的原始程式碼會編譯成符合 CLI 規格的中繼語言 (IL))
※LIKE & IN
LIKE(是string的方法 Contains() )
string[] data = { "aaaa", "bbbb", "cccc" };
var result = from s1 in data
where s1.Contains("b")
select s1;
foreach (var item in result)
{
Console.WriteLine(item);
}
IN(LINQ的擴充方法Contains())
string[] data = { "aaaa", "bbbb", "cccc" };
string[] infilter = { "bbbb", "cccc" };
var result = from item in data
where infilter.Contains(item)
select item;
foreach (var item in result)
{
Console.WriteLine(item);
}
## LINQ是後面的語法向前面要資料
※OrderBy & ThenBy
OrderBy(迴圈,會先抓取所有元素比較再排序) => 會影響效能
OrderBy會判斷前後的LINQ
OrderBy再OrderBy(是兩個迴圈)
OrderBy再ThenBy(判斷後面要資料的是否為ThenBy=>若為ThenBy則會結合OrderBy為一個迴圈跑完)
※First & Last
FirstOrDefault & LastOrDefault(沒有資料會回傳有效值)
int實作List,因此會直接取得最後一筆,因實作List會找到最後索引值
多數情況下會從前面找到後面,再取得最後一筆。
Last & LastOrDefault =>
Eentity Framework不支援,因為SQL Server 沒有Bottom這個指令可以使用
var data = new List<int>();
var r = new Random((int)DateTime.Now.Ticks);
for (int i = 0; i < 1000000; i++)
{
data.Add(r.Next(1, 100));
}
Stopwatch sw = new Stopwatch();
sw.Start();
//IEnumerable
var result = data.Where(a => a > 3).LastOrDefault();
sw.Stop();
Console.WriteLine($"{result} {sw.ElapsedMilliseconds}");
印出結果:67 43
var data = new List<int>();
var r = new Random((int)DateTime.Now.Ticks);
for (int i = 0; i < 1000000; i++)
{
data.Add(r.Next(1, 100));
}
var list = data.Where(a => a > 3).ToList();
Stopwatch sw = new Stopwatch();
sw.Start();
//List
var result = list.LastOrDefault();
sw.Stop();
Console.WriteLine($"{result} {sw.ElapsedMilliseconds}");
印出結果:64 0
※Single
取得單一
※ElementAt、ElementAtOrDefault
利用索引值取得資料
※取部分集合類函式
最常使用SELECT取得部分資料。
//可取出索引子
var result = dataArray.Select((s, index) => $"{s},{index}");
foreach (var item in result)
{
Console.WriteLine(item);
}
//WHERE Index 從0開始
var result = dataArray.Where(item=>item > 3).Select((s, index) => $"{s},{index}");
foreach (var item in result)
{
Console.WriteLine(item);
}
※SelectMany
若有巢狀迴圈值可思考是否可改成SelectMany
var dep1 = new Department()
{
Name = "IT",
Persons = new List<Person>()
{
new Person()
{
Name = "code6421",
Age = 10
},
new Person()
{
Name = "mary",
Age = 11
}
}
};
var dep2 = new Department()
{
Name = "RD",
Persons = new List<Person>()
{
new Person()
{
Name = "mark",
Age = 12
}
}
};
List<Department> deps = new List<Department>() { dep1, dep2 };
foreach(var dev in deps)
{
foreach(var person in dev.Persons)
{
Console.WriteLine($"{person.Name}");
}
}
SelectMany可省一個迴圈
foreach (var item in deps.SelectMany(item => item.Persons))
{
Console.WriteLine($"{item.Name}");
}
※Let
//Console中印出結果
foreach (var item in FindInFilesLinq("C:\\Windows","driver"))
{
Console.WriteLine(item);
}
//直覺函式
static List<string> FindInFiles(string directory,string query)
{
var result = new List<string>();
foreach(var item in Directory.GetFiles(directory, "*.ini"))
{
var content = File.ReadAllText(item);
if (content.Contains(query))
{
result.Add(item);
}
}
return result;
}
//改為LINQ函式
static List<string> FindInFilesLinq(string directory, string query)
{
var files = Directory.GetFiles(directory, "*.ini");
var result = files
.Where(item => File.ReadAllText(item).Contains(query));
return result.ToList();
}
※Into
左邊的結果往右邊拋
※DefaultIfEmpty(LeftJoin)
當集合為空時,又需要右邊資料時將其塞空值丟回,其餘資料即可印出
※Take & Skip(可做分頁)
Skip & Take 是以Element為主,從"1"開始
※TakeWhile & SkipWhile(很少用)
※Except & Intersect(常用,EX:分析使用者流存率報表)
Except(排除相同元素)
Intersect(取得相同元素)