讓他代我說:Delegate 的溫柔設計學
在人生裡,我們總在不知不覺中進行著「委派」。不是公司開會那種,而是日常裡那些再自然不過的小任務外包。
👦:「阿嬤,我要出門了,幫我跟媽媽說我會晚點回家~」你沒直接說,而是把「傳話」這件事交給了阿嬤。這一刻,你不是孫子,你是個指派 callback 的主控者。
而在程式世界裡,這樣的行為就叫作:delegate ——拜託別人幫你做某事,但怎麼做、什麼時候做,全憑對方自由發揮。
123456789101112131415161718//// 定義要執行的任務的事件樣貌delegate void PassMessage(string message);public class You{ public void AskSomeoneToPassMessage(PassMessage messenger) { messenger("我會晚一點回家"); }}//// 執行者自己決定怎麼執行void GrandmaPassMessage(string msg){ Console.Writ ...
Deep Clone
還記得小時候,我總覺得「鏡中的自己」很神奇。他會跟著我一起笑、一起生氣、一起模仿我的每個動作。但漸漸長大才明白——那並不是真正的「另一個我」,而只是一個淺薄的影子。
程式世界裡,物件的複製也是這樣。有時候,我們只是得到了「鏡子裡的自己」(Shallow Copy),稍不注意,動到這邊,另一邊也跟著被牽連。而有時候,我們會想要的是一個「能獨立走出鏡子」的自己(Deep Copy),擁有完全不同的記憶體、不同的命運。
🌺 Shallow Copy & Deep Copy
左圖:兩個物件其實共用相同的記憶體位址,所以改一個,另一個也會受影響。
右圖:透過 Deep Copy,物件被完整複製,擁有獨立的記憶體,不會互相干涉。
🌺 序列化進行 Deep Copy因為 反序列化一定會產生全新的物件,所以天然就避免了 Shallow Copy 的共享問題。
不須理會屬性的異動
方法簡單只要一個「擴充方法」就能對任何型別用,不用每個 class 寫 Clone()。這比 AutoMapper 少了一些設定,入門更快。
只能複製相同型別DeepCloneByJson() 就只 ...
Asynchronous Programming - 第七章:非同步之林:執行緒的四季輪迴
在這片名為非同步之林的廣闊森林裡,藏著一家智慧餐廳。這間餐廳一天到晚接單不斷,每張訂單都是一個任務,廚房裡的人力有限,卻得讓所有客人都吃得快又好、不讓誰在門口久等,還得同時兼顧廚房資源的高效運用。
這家餐廳裡,有忙碌的櫃台接待員(Main Thread),有後廚大廚團隊(Worker Threads),有專門通知外送到達的取餐小組(I/O Completion Threads),偶爾,老闆還會臨時請來私廚(Custom Thread),用一場又一場執行緒的調度,撐起這座非同步之林裡飲食服務。
🎵 Main Thread = 櫃台接待員負責第一時間接待客人、記錄每張訂單、回答客人問題。櫃台小姐(Main Thread)不能被卡住,一卡住,後面來的客人全部排隊大塞車!所以櫃台只是登記完,馬上就把訂單丟給後廚(ThreadPool)去做。
✅ 在 ASP.NET 裡,Main Thread 就像每次收到使用者的 HTTP 請求的 Request Thread;桌面程式裡就是 UI 執行緒,負責畫面互動。
🎵 Worker Threads = ...
Asynchronous Programming - 第八章:一座池子的呼吸
在雲端深處,棲息著一個無形的池子,我們稱它為 ThreadPool——它不眠不休,日夜吞吐無數任務,是城市裡最沉默卻最可靠的工廠。每一個呼叫 Task.Run 的瞬間,就像把工作單投入池子,任它激起層層漣漪,有人接手、有人閒置、有人待命。
我們在這裡低聲詠唱,希望當高峰來臨,池子能無限擴張,當潮水退去,池子也能自我收束,忠實而溫順,既不餓死任務,也不浪費資源。這是我們寫給池子的詠歌,也是給未來的自己一段溫柔的提醒 —— 當你想起這座池子,請記得,它從未離開,只是默默在背後,調度著一切的並行與秩序。
🎵 Task.Run 觀察 ThreadPool 的重複使用12345678910111213141516171819202122232425262728293031323334353637void Main(){ Task.Run(() => DoSomething()); Task.Run(() => DoSomething()); Task.Run(() => DoSomething()); Task.Run(() => DoSomething ...
Deferred Execution - 延遲的智慧
延遲執行(Deferred Execution)
他是一種耐心的智慧 —— 明明寫下一段 Code,卻不急著執行,彷彿在等待一個更合適的時機 !?
延遲執行(Deferred Execution)
幫助你在被某句話刺到時,第一反應不是想要立刻回嘴、發怒或做出回應。不選擇這種「立即執行」的行為,防止你在關係上造成傷害或是事後後悔
延遲執行(Deferred Execution)
讓我們理解,重大決定前需要沉澱期,轉職、搬家、投資、進入一段關係…… 不適合因為他人的壓力就出發立即執行
而創意與靈感的生成過程,很多時候你以為自己沒有靈感,其實是「狀態機還沒執行」。你已經把表達式建好了,放著沒動,等到靈感來臨時,才會開始真正地「MoveNext()」。
我們不是缺靈感,只是活不夠久,讓它進入可被遍歷的狀態
實作 : 靈感的延遲執行人生就是一段靈感蒐集箱
1234567891011121314151617181920212223242526public class InspirationCollector : IEnu ...
Asynchronous Programming - 第六章:時間碎片裡的員工們
🎵 Program、Process、Thread想像你是一位準備開店的廚師,桌上攤開一本厚重的菜譜——這就是你的 Program(程式碼集合)。它記錄了每道菜的做法、調味的比例、上菜的順序……但這些指示仍停留在紙上,尚未進入現實。它像是還未走進世界的夢,只存在於設計之中。
當你真正開張營業、招呼客人,這份菜譜就被實體化成了一間 Process(處理程序)—— 一間運作中的餐廳。這間餐廳開始佔據空間(記憶體)、使用瓦斯爐(CPU)、冰箱(硬碟),並啟動了你的夢想。每一間正在運作的餐廳,都是一個獨立的 Process,就像你電腦上同時開著 Word、Chrome 和 Spotify,一家店煮咖哩、一家店沖咖啡,彼此不會互相干擾。。
但餐廳開起來只是第一步,真正讓整個流程流動的,是餐廳裡的「人」—— 這些人,正是 Thread(執行緒)。一家只有一位大廚的小餐館,也許只能一張一張煎魚、一道一道上菜(單執行緒);但一家人潮洶湧的熱炒店,就需要多位大廚同時開火、服務生穿梭上菜(多執行緒),讓整個店面井然有序地高速運作。
在一個開發工具(像 Visual Studio)裡,一個 Threa ...
Asynchronous Programming - 第五章:錯誤等待的死鎖之吻
他們曾經相愛,也曾經深信,只要彼此都願意等,愛就會回來。
她把訊息輸入對話框,卻沒有按下傳送,想等他主動說第一句話。她想:「他如果真的在乎,就會找我。」他打開視窗看了又看,也沒傳訊息,心想:「她如果還有感覺,就會先聯絡我。」
他們每天打開彼此的聊天室又關上,不是沒有思念,而是不願先伸出手。怕輸、怕低頭、怕被拒絕。於是他們靜靜地等,在各自的世界,畫了一個誰也跨不過的等待邊界。
這不是不愛,是兩個 .Result() 卡在彼此門前的靈魂,沒有人願意給對方 await 的空間。
🎵 問題緣起:為什麼 .Result 有時會讓整個程式卡住?async deadlock
await 與 Task.Result/Task.Wait () 的 Deadlock 問題
在 ASP.NET 舊版框架(非 Core)中,如果你在同步方法中呼叫非同步方法,像這樣:
1var result = GetDataAsync().Result;
你可能會遇到一個非常棘手的問題:死鎖(Deadlock)。
🎵 死鎖是怎麼發生的?ASP.NET Framework 中的流程:
主執行緒呼叫 .Res ...
Asynchronous Programming - 第四章:彼岸未歸的異步任務
她在深夜寫好了一段訊息,對著那個熟悉的對話框,輕聲按下「傳送」。
網路卡住了,畫面上顯示「正在傳送…」,她盯著那行字等了一秒、兩秒,終究關上了手機,心想:「應該已經送出了吧。」
隔天,她等不到回應。那人說什麼都沒收到。
她反覆打開訊息記錄,那串話仍顯示為「未送達」,像是從未存在過——但她明明說過了。
一個沒有被接收的訊息,不會自動變成世界的理解;一個沒有被等待的結果,也無法保證會如你所想地完成。
🎵 非同步的錯誤處理:等待與錯誤的交錯人生在實務開發中,我們經常面臨一種情境:必須等待某個非同步任務完成,才能繼續執行接下來的邏輯。但如果我們當下的環境是同步方法(例如 Main() 或某些事件處理函式),該怎麼辦?
這時,許多開發者會選擇以下方式強行「同步化」等待:
.Wait()
.Result
GetAwaiter().GetResult()
它們都會強迫等待 Task 結束,但錯誤處理機制卻有天壤之別,而這個差異,正是許多初學者第一次遇到的陷阱。以下,我們將模仿實驗,實際觀察其錯誤行為與影響。
實驗出處
🎵 實驗12345678910111213141516171819202 ...
刷新之間,藏著多少重來與等待 - 網頁快取
清晨七點半,你手捧著一杯還冒著熱氣的黑咖啡,筆電剛開機,PChome 的首頁已經靜靜等著。今天,是傳說中的限量開搶日 —— PS5 Spider-Man 2 限定版主機,早上八點整正式開賣。明明一個月前,你才下定決心要戒掉電動,結果看到廣告那一瞬間,理智直接下線:「反正只是收藏,不一定要打開來玩。」這麼說服自己之後,還特地設了三個鬧鐘,把商品頁加進書籤,信用卡也提前擺在桌上待命。
滑鼠懸停在「重新整理」按鈕上,指尖微微冒汗,內心默唸著:「這不算破戒,這叫做支持正版!」
當秒針跳到 7:59:59,你一口氣按下刷新,畫面瞬間閃動。那張帥氣的限量主機圖,早已從最近的新竹 CDN Edge Server 快取而來,商品庫存與折扣倒數,則剛剛從台北資料中心即時更新,因為你的瀏覽器在快取過期後,發送了 If-Modified-Since 的精確詢問。至於整個商品頁的 HTML 和 CSS,早就在昨天深夜「練習預購」的時候,被你的硬碟 Disk Cache 偷偷保存了下來,靜靜等著這個命運的早晨。
從源伺服器,到全球分散的 CDN 節點,再到本地硬碟與記憶體,資料在無數快取層之間跳躍、檢查、確認, ...
Content Delivery Network(CDN)
在一個大熱天的午後,你走在勤美綠園道上,突然想吃點冰冰涼涼的甜點。你走了 3 分鐘,到附近的全家便利商店,打開冰櫃看到一盒寫著「東京限定布丁」的商品,決定嚐嚐這份來自職人之國的綿密滋味。
等等——為什麼我在台中,卻能在便利商店買到「東京限定」的布丁?
如果是從日本工廠直寄過來,理論上要等上好幾天、還可能被海關卡住。但今天我們能即時吃到,是因為:
🎉「全家便利商店」早就跟日本合作,把布丁大量送到全台分店的冷藏庫裡,你走路 3 分鐘就能買到!
🍮 用布丁理解 CDN:就近提供內容這就像是 CDN(內容傳遞網路) 的概念:
CDN 預先把內容(如圖片、影片、靜態網頁)複製到遍佈世界各地的節點,當使用者要存取網站時,就能從最近的節點取得資料,而不用跨越半個地球回到原始主機。
📦 布丁=靜態資源🏪 全家分店=CDN 節點🏭 日本原廠=你的主機(origin server)
👾 技術面看 CDN Cache 怎麼運作?以我們維護的網站 example.com 為例,若設定 CDN 快取行為,流程大致如下:
使用者首次造訪網站,CDN 上尚無該資料 → ...