[{"data":1,"prerenderedAt":12290},["ShallowReactive",2],{"notes-list":3},[4,79,888,989,2550,2652,2667,2995,4147,4493,4956,6297,7947,8209,8602,8646,9257,9342,9733,10336,10608,11094,11248,11712,11838],{"id":5,"title":6,"body":7,"category":64,"date":65,"description":66,"draft":67,"extension":68,"meta":69,"navigation":70,"path":71,"seo":72,"sourcePath":73,"stem":74,"tags":75,"__hash__":78},"notes\u002Fnotes\u002Fsync\u002Fcsharp-linq-查詢.md","查詢",{"type":8,"value":9,"toc":59},"minimark",[10,19,24,27,51,55],[11,12,13,14,18],"p",{},"依據",[15,16,17],"strong",{},"使用頻率與應用場景","，最常用的方法可以分為以下 6 大核心分類：",[20,21,23],"h3",{"id":22},"篩選與過濾最基本門檻","篩選與過濾（最基本門檻）",[11,25,26],{},"``",[28,29,34],"pre",{"className":30,"code":31,"language":32,"meta":33,"style":33},"language-csharp shiki shiki-themes github-light github-dark","\u002F\u002F 撈出未被刪除且年滿 18 的使用者\nvar activeUsers = users.Where(u => !u.IsDeleted && u.Age >= 18);\n","csharp","",[35,36,37,45],"code",{"__ignoreMap":33},[38,39,42],"span",{"class":40,"line":41},"line",1,[38,43,44],{},"\u002F\u002F 撈出未被刪除且年滿 18 的使用者\n",[38,46,48],{"class":40,"line":47},2,[38,49,50],{},"var activeUsers = users.Where(u => !u.IsDeleted && u.Age >= 18);\n",[20,52,54],{"id":53},"_2-挑選欄位與格式轉換資料加工","2. 挑選欄位與格式轉換（資料加工）",[56,57,58],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":33,"searchDepth":47,"depth":47,"links":60},[61,63],{"id":22,"depth":62,"text":23},3,{"id":53,"depth":62,"text":54},"backend","2026-08-24","依據使用頻率與應用場景，最常用的方法可以分為以下 6 大核心分類：",false,"md",{},true,"\u002Fnotes\u002Fsync\u002Fcsharp-linq",{"title":6,"description":66},"C#\u002FLINQ\u002F查詢.md","notes\u002Fsync\u002Fcsharp-linq-查詢",[76,77],"C#","LINQ","jHwjVU6UpueMyX3hTsSROmqRTV85ZvShxFw1hx3-nX4",{"id":80,"title":81,"body":82,"category":64,"date":65,"description":879,"draft":67,"extension":68,"meta":880,"navigation":70,"path":881,"seo":882,"sourcePath":883,"stem":884,"tags":885,"__hash__":887},"notes\u002Fnotes\u002Fsync\u002Fcsharp-易混淆概念-易混淆概念04.md","易混淆概念04",{"type":8,"value":83,"toc":858},[84,88,97,118,121,151,271,274,278,289,298,355,358,368,380,386,408,410,414,417,449,453,528,532,550,553,578,581,604,607,611,647,651,686,693,731,738,798,800,804,812,826,828,831,835,844,847,856],[85,86,87],"h2",{"id":87},"擴充方法",[89,90,91],"blockquote",{},[11,92,93,94],{},"關鍵字 ",[35,95,96],{},"this",[89,98,99],{},[11,100,101,102,105,106,117],{},"在方法參數裡寫 ",[35,103,104],{},"this string str","，只是在向編譯器下指令：",[15,107,108,109,112,113,116],{},"「請允許工程師把第一個參數 ",[35,110,111],{},"str"," 挪到最前面，用 ",[35,114,115],{},"str.方法名稱()"," 的方式來呼叫它。」"," 它不是物件引用自己，純粹是呼叫語法的左右位置對調而已。",[20,119,120],{"id":120},"擴充方法的語法規則",[122,123,124,134,143],"ul",{},[125,126,127,128,133],"li",{},"必須放在一個 ",[15,129,130],{},[35,131,132],{},"public static class"," 裡面。",[125,135,136,137,142],{},"方法本身必須是 ",[15,138,139],{},[35,140,141],{},"public static","。",[125,144,145,150],{},[15,146,147,148],{},"第一個參數前加上 ",[35,149,96],{},"，代表這個方法是「擴充給哪一種型別」使用的。",[28,152,154],{"className":30,"code":153,"language":32,"meta":33,"style":33},"public static class LogAnalysis\n{\n    public static string SubstringAfter(this string str, string delimiter)\n    {\n        \u002F\u002F 1. 找到分隔符號在字串中的起始索引\n        int index = str.IndexOf(delimiter);\n\n        \u002F\u002F 2. 如果找不到，通常回傳空字串或原字串（視需求而定）\n        if (index == -1)\n        {\n            return string.Empty;\n        }\n\n        \u002F\u002F 3. 起始位置要跳過 delimiter 本身的長度\n        int startIndex = index + delimiter.Length;\n\n        \u002F\u002F 4. 從該位置一路截取到最後\n        return str.Substring(startIndex);\n    }\n}\n",[35,155,156,161,166,171,177,183,189,195,201,207,213,219,225,230,236,242,247,253,259,265],{"__ignoreMap":33},[38,157,158],{"class":40,"line":41},[38,159,160],{},"public static class LogAnalysis\n",[38,162,163],{"class":40,"line":47},[38,164,165],{},"{\n",[38,167,168],{"class":40,"line":62},[38,169,170],{},"    public static string SubstringAfter(this string str, string delimiter)\n",[38,172,174],{"class":40,"line":173},4,[38,175,176],{},"    {\n",[38,178,180],{"class":40,"line":179},5,[38,181,182],{},"        \u002F\u002F 1. 找到分隔符號在字串中的起始索引\n",[38,184,186],{"class":40,"line":185},6,[38,187,188],{},"        int index = str.IndexOf(delimiter);\n",[38,190,192],{"class":40,"line":191},7,[38,193,194],{"emptyLinePlaceholder":70},"\n",[38,196,198],{"class":40,"line":197},8,[38,199,200],{},"        \u002F\u002F 2. 如果找不到，通常回傳空字串或原字串（視需求而定）\n",[38,202,204],{"class":40,"line":203},9,[38,205,206],{},"        if (index == -1)\n",[38,208,210],{"class":40,"line":209},10,[38,211,212],{},"        {\n",[38,214,216],{"class":40,"line":215},11,[38,217,218],{},"            return string.Empty;\n",[38,220,222],{"class":40,"line":221},12,[38,223,224],{},"        }\n",[38,226,228],{"class":40,"line":227},13,[38,229,194],{"emptyLinePlaceholder":70},[38,231,233],{"class":40,"line":232},14,[38,234,235],{},"        \u002F\u002F 3. 起始位置要跳過 delimiter 本身的長度\n",[38,237,239],{"class":40,"line":238},15,[38,240,241],{},"        int startIndex = index + delimiter.Length;\n",[38,243,245],{"class":40,"line":244},16,[38,246,194],{"emptyLinePlaceholder":70},[38,248,250],{"class":40,"line":249},17,[38,251,252],{},"        \u002F\u002F 4. 從該位置一路截取到最後\n",[38,254,256],{"class":40,"line":255},18,[38,257,258],{},"        return str.Substring(startIndex);\n",[38,260,262],{"class":40,"line":261},19,[38,263,264],{},"    }\n",[38,266,268],{"class":40,"line":267},20,[38,269,270],{},"}\n",[272,273],"hr",{},[85,275,277],{"id":276},"ienumerable-可被走訪可迭代的能力","IEnumerable 可被走訪（可迭代）」的能力",[89,279,280],{},[11,281,282,285,286,142],{},[35,283,284],{},"IEnumerable"," 的意思純粹是：",[15,287,288],{},"「你可以拿放大鏡從頭到尾看過我身上的每一個字元」",[89,290,291],{},[11,292,293,294,297],{},"LINQ 只認 ",[35,295,296],{},"IEnumerable\u003CT>","，不認具體型別。",[122,299,300,313,331,347],{},[125,301,302,305,306,309,310,312],{},[15,303,304],{},"統一介面","：只要能被 ",[35,307,308],{},"foreach"," 走訪（實作了 ",[35,311,284],{},"），就能直接掛上 LINQ。",[125,314,315,318,319,322,323,326,327,330],{},[15,316,317],{},"底層無關","：不論是 ",[35,320,321],{},"string","、",[35,324,325],{},"char[]"," 還是 ",[35,328,329],{},"List","，對 LINQ 來說都是同一種「可迭代的資料流」。",[125,332,333,336,337,322,340,343,344,346],{},[15,334,335],{},"隨插即用","：",[35,338,339],{},".Select()",[35,341,342],{},".Where()"," 等所有擴充方法，本質上都是寫給 ",[35,345,296],{}," 的通用工具箱。",[125,348,349,336,352,354],{},[15,350,351],{},"唯讀瀏覽",[35,353,284],{}," 只提供「唯讀瀏覽」權限",[11,356,357],{},"============",[20,359,361,362,364,365,367],{"id":360},"字串string本身就是一個字元的集合char","字串（",[35,363,321],{},"）本身就是一個字元的集合（",[35,366,325],{},"）",[89,369,370],{},[11,371,372,373,376,377,379],{},"它的底層儲存結構是連續的 ",[35,374,375],{},"char"," 記憶體區塊，但它在型別上不是 ",[35,378,325],{},"，而是一個獨立的物件。",[11,381,382,383,385],{},"因為 ",[35,384,321],{}," 實作了陣列最核心的兩大特徵：",[387,388,389,392],"ol",{},[125,390,391],{},"可以用索引讀取（Indexer）",[125,393,394,395,398,399,401,402,322,405,407],{},"實作了 ",[35,396,397],{},"IEnumerable\u003Cchar>","： 這代表它可以像陣列一樣被 ",[35,400,308],{}," 走訪，也可以直接套用所有 LINQ 方法（如 ",[35,403,404],{},".Count()",[35,406,342],{},"）。",[272,409],{},[85,411,413],{"id":412},"時間用法datetime","時間用法DateTime",[20,415,416],{"id":416},"建構與解析",[122,418,419,431],{},[125,420,421,336,424,427,428,407],{},[15,422,423],{},"手動指定",[35,425,426],{},"new DateTime(year, month, day)","（時分秒預設自動歸零為 ",[35,429,430],{},"00:00:00",[125,432,433,436,437],{},[15,434,435],{},"字串轉時間","：\n",[122,438,439,444],{},[125,440,441],{},[35,442,443],{},"DateTime.Parse(str)",[125,445,446],{},[35,447,448],{},"DateTime.TryParse(str, out var dt)",[20,450,452],{"id":451},"格式化代號大小寫鐵則tostring","格式化代號大小寫鐵則（ToString）",[122,454,455,472,493],{},[125,456,457,460,461,464,465,468,469,142],{},[35,458,459],{},"MM","（大寫）是",[15,462,463],{},"月份","；",[35,466,467],{},"mm","（小寫）是",[15,470,471],{},"分鐘",[125,473,474,477,478,464,481,484,485,488,489,492],{},[35,475,476],{},"HH","（大寫）是 ",[15,479,480],{},"24 小時制",[35,482,483],{},"hh","（小寫）是 ",[15,486,487],{},"12 小時制","（搭配 ",[35,490,491],{},"tt"," 顯示 AM\u002FPM）。",[125,494,495,496,322,499,322,502,505,506,509,510,513,514,322,516,322,519,505,521,509,524,527],{},"單碼 ",[35,497,498],{},"M",[35,500,501],{},"d",[35,503,504],{},"h"," ",[15,507,508],{},"不補零","（例如 ",[35,511,512],{},"7"," 月）；雙碼 ",[35,515,459],{},[35,517,518],{},"dd",[35,520,483],{},[15,522,523],{},"自動補零",[35,525,526],{},"07"," 月）。",[20,529,531],{"id":530},"物件不可變性immutable","物件不可變性（Immutable）",[122,533,534,540],{},[125,535,536,537,407],{},"時間不能直接修改屬性（不能 ",[35,538,539],{},"dt.Year = 2026",[125,541,542,543,546,547,407],{},"加減時間必須使用方法並",[15,544,545],{},"接收回傳的新物件","（例如：",[35,548,549],{},"dt = dt.AddDays(1)",[20,551,552],{"id":552},"時間比對與運算",[122,554,555,562],{},[125,556,557,558,561],{},"比較先後直接用運算子：",[35,559,560],{},"appointment \u003C DateTime.Now","（是否已過期）。",[125,563,564,565,546,570,573,574,577],{},"兩個時間相減會得到 ",[15,566,567],{},[35,568,569],{},"TimeSpan",[35,571,572],{},"TimeSpan diff = date2 - date1;","，可用 ",[35,575,576],{},"diff.TotalDays"," 取得天數差）。",[20,579,580],{"id":580},"歸零與常用屬性",[122,582,583,591],{},[125,584,585,588,589,142],{},[35,586,587],{},".Date","：快速取得當天午夜 ",[35,590,430],{},[125,592,593,596,597,600,601,407],{},[35,594,595],{},".Hour","：內部永遠以 ",[15,598,599],{},"0 ~ 23 的整數"," 儲存，判斷上下午或營業時段最方便（例如 ",[35,602,603],{},"dt.Hour >= 12",[20,605,606],{"id":606},"常用方法",[608,609,610],"h4",{"id":610},"靜態屬性與取得當前時間",[122,612,613,619,625,637],{},[125,614,615,618],{},[35,616,617],{},"DateTime.Now","：目前的當地時間（含時分秒）。",[125,620,621,624],{},[35,622,623],{},"DateTime.UtcNow","：目前的 UTC 標準時間（伺服器\u002F跨時區常用）。",[125,626,627,630,631,633,634,407],{},[35,628,629],{},"DateTime.Today","：今天的午夜零點 ",[35,632,430],{},"（相當於 ",[35,635,636],{},"DateTime.Now.Date",[125,638,639,642,643,646],{},[35,640,641],{},"DateTime.MinValue"," \u002F ",[35,644,645],{},"DateTime.MaxValue","：C# 支援的最小時間（0001\u002F1\u002F1）與最大時間（9999\u002F12\u002F31）。",[608,648,650],{"id":649},"解析與字串轉換靜態方法","解析與字串轉換（靜態方法）",[122,652,653,658,668,674,680],{},[125,654,655,657],{},[35,656,443],{},"：字串轉時間（格式不對會拋出例外）。",[125,659,660,663,664,667],{},[35,661,662],{},"DateTime.TryParse(str, out dt)","：安全轉換，回傳 ",[35,665,666],{},"bool"," 代表成功與否。",[125,669,670,673],{},[35,671,672],{},"DateTime.ParseExact(str, format, ...)","：指定嚴格格式進行解析。",[125,675,676,679],{},[35,677,678],{},"DateTime.DaysInMonth(year, month)","：查詢某年某月一共有幾天（自動算閏年）。",[125,681,682,685],{},[35,683,684],{},"DateTime.IsLeapYear(year)","：檢查是否為閏年。",[608,687,689,690,367],{"id":688},"時間加減與操作物件實例方法-dtxxx","時間加減與操作（物件實例方法 ",[35,691,692],{},"dt.XXX()",[122,694,695,707,719,725],{},[125,696,697,642,700,642,703,706],{},[35,698,699],{},"dt.AddYears(n)",[35,701,702],{},"dt.AddMonths(n)",[35,704,705],{},"dt.AddDays(n)","：加減年月日。",[125,708,709,642,712,642,715,718],{},[35,710,711],{},"dt.AddHours(n)",[35,713,714],{},"dt.AddMinutes(n)",[35,716,717],{},"dt.AddSeconds(n)","：加減時分秒。",[125,720,721,724],{},[35,722,723],{},"dt.Add(timeSpan)","：直接加上一段自訂時間長度。",[125,726,727,730],{},[35,728,729],{},"dt.ToString(\"格式\")","：轉成自訂格式字串。",[608,732,734,735,367],{"id":733},"提取內部數值物件實例屬性-dtxxx","提取內部數值（物件實例屬性 ",[35,736,737],{},"dt.XXX",[122,739,740,752,767,776,782,790],{},[125,741,742,322,745,322,748,751],{},[35,743,744],{},"dt.Year",[35,746,747],{},"dt.Month",[35,749,750],{},"dt.Day","：取得整數的年月日。",[125,753,754,322,757,322,760,322,763,766],{},[35,755,756],{},"dt.Hour",[35,758,759],{},"dt.Minute",[35,761,762],{},"dt.Second",[35,764,765],{},"dt.Millisecond","：取得整數的時分秒毫秒。",[125,768,769,772,773,407],{},[35,770,771],{},"dt.DayOfWeek","：取得星期幾（回傳列舉，如 ",[35,774,775],{},"DayOfWeek.Monday",[125,777,778,781],{},[35,779,780],{},"dt.DayOfYear","：這一天是當年的第幾天（1 ~ 366）。",[125,783,784,787,788,142],{},[35,785,786],{},"dt.Date","：保留年月日，時分秒全部歸零為 ",[35,789,430],{},[125,791,792,795,796,407],{},[35,793,794],{},"dt.TimeOfDay","：單獨取出當天的時間段（回傳 ",[35,797,569],{},[272,799],{},[85,801,803],{"id":802},"dictionary跟hashset","Dictionary跟HashSet",[89,805,806],{},[11,807,808,809],{},"把它們想成是同一種底層技術的「雙胞胎」，差別只在於 ",[15,810,811],{},"有沒有附帶 Value",[89,813,814],{},[11,815,816,825],{},[15,817,818,821,822],{},[35,819,820],{},"HashSet\u003CT>"," 本質上就是一個「Value 為空（不需要附帶資料）」的特化版 ",[35,823,824],{},"Dictionary","，因為省去了存 Value 的空間，它比 Dictionary 更省記憶體且更輕量。",[272,827],{},[85,829,830],{"id":830},"寫法",[20,832,834],{"id":833},"陣列初始設定式array-initializer","陣列初始設定式（Array Initializer）",[28,836,838],{"className":30,"code":837,"language":32,"meta":33,"style":33},"string[] words = { \"apple\", \"banana\", \"apple\", \"orange\", \"banana\", \"apple\" };\n",[35,839,840],{"__ignoreMap":33},[38,841,842],{"class":40,"line":41},[38,843,837],{},[11,845,846],{},"以下是標準寫法：",[28,848,850],{"className":30,"code":849,"language":32,"meta":33,"style":33},"string[] words = new string[] { \"apple\", \"banana\", \"apple\" };\n",[35,851,852],{"__ignoreMap":33},[38,853,854],{"class":40,"line":41},[38,855,849],{},[56,857,58],{},{"title":33,"searchDepth":47,"depth":47,"links":859},[860,863,867,875,876],{"id":87,"depth":47,"text":87,"children":861},[862],{"id":120,"depth":62,"text":120},{"id":276,"depth":47,"text":277,"children":864},[865],{"id":360,"depth":62,"text":866},"字串（string）本身就是一個字元的集合（char[]）",{"id":412,"depth":47,"text":413,"children":868},[869,870,871,872,873,874],{"id":416,"depth":62,"text":416},{"id":451,"depth":62,"text":452},{"id":530,"depth":62,"text":531},{"id":552,"depth":62,"text":552},{"id":580,"depth":62,"text":580},{"id":606,"depth":62,"text":606},{"id":802,"depth":47,"text":803},{"id":830,"depth":47,"text":830,"children":877},[878],{"id":833,"depth":62,"text":834},"關鍵字 `this`",{},"\u002Fnotes\u002Fsync\u002Fcsharp-04",{"title":81,"description":879},"C#\u002F易混淆概念\u002F易混淆概念04.md","notes\u002Fsync\u002Fcsharp-易混淆概念-易混淆概念04",[76,886],"Concepts","tJ4kph3J8xTWctsXASN2w5nOXtMe_9gei_VPmq0ITiI",{"id":889,"title":890,"body":891,"category":64,"date":981,"description":982,"draft":67,"extension":68,"meta":983,"navigation":70,"path":71,"seo":984,"sourcePath":985,"stem":986,"tags":987,"__hash__":988},"notes\u002Fnotes\u002Fsync\u002Fcsharp-linq-概述.md","概述",{"type":8,"value":892,"toc":978},[893,897,906,926,929],[85,894,896],{"id":895},"enumerable","Enumerable",[89,898,899],{},[11,900,901,902,905],{},"它是整個 C# ",[15,903,904],{},"LINQ（Language Integrated Query）"," 的大本營。",[11,907,908,909,322,911,322,913,916,917,919,920,142],{},"日常寫在陣列或 List 後面的 ",[35,910,342],{},[35,912,339],{},[35,914,915],{},".Sum()"," 等等，底層全部都是 ",[35,918,896],{}," 類別所提供的",[15,921,922,923,367],{},"擴充方法（",[35,924,925],{},"this IEnumerable\u003CT>",[608,927,928],{"id":928},"主要用途分類",[387,930,931,957],{},[125,932,933,934],{},"產生器靜態方法（從無到有造序列）",[122,935,936,942,951],{},[125,937,938,941],{},[35,939,940],{},"Enumerable.Range(start, count)","：產生連續整數",[125,943,944,947,948],{},[35,945,946],{},"Enumerable.Repeat(\"Hello\", 3)","：重複元素，產生 ",[35,949,950],{},"[\"Hello\", \"Hello\", \"Hello\"]",[125,952,953,956],{},[35,954,955],{},"Enumerable.Empty\u003Cint>()","：產生一個乾淨且零記憶體配置的空序列",[125,958,959,960,977],{},"擴充方法（類似 JavaScript 陣列的高階函式）",[122,961,962],{},[125,963,964,965,967,968,322,971,322,974,976],{},"所有實作了 ",[35,966,296],{}," 的集合（陣列 ",[35,969,970],{},"int[]",[35,972,973],{},"List\u003CT>",[35,975,824],{},"）都可以直接串接這些方法：","\n ",{"title":33,"searchDepth":47,"depth":47,"links":979},[980],{"id":895,"depth":47,"text":896},"2026-08-22","它是整個 C# LINQ（Language Integrated Query） 的大本營。",{},{"title":890,"description":982},"C#\u002FLINQ\u002F概述.md","notes\u002Fsync\u002Fcsharp-linq-概述",[76,77],"5QDsozIBXrL6kKfRNVm3L_McF_Uq46UEUx3uatE3-i8",{"id":990,"title":991,"body":992,"category":64,"date":981,"description":1004,"draft":67,"extension":68,"meta":2543,"navigation":70,"path":2544,"seo":2545,"sourcePath":2546,"stem":2547,"tags":2548,"__hash__":2549},"notes\u002Fnotes\u002Fsync\u002Fcsharp-易混淆概念-易混淆概念01.md","易混淆概念01",{"type":8,"value":993,"toc":2502},[994,998,1002,1005,1016,1025,1028,1031,1035,1040,1044,1057,1083,1093,1139,1141,1145,1150,1163,1272,1276,1281,1305,1310,1319,1323,1328,1333,1336,1339,1344,1352,1354,1367,1370,1373,1382,1385,1394,1406,1415,1420,1422,1426,1430,1438,1441,1445,1464,1466,1470,1474,1482,1486,1497,1501,1504,1507,1510,1515,1523,1527,1638,1640,1644,1647,1658,1661,1664,1667,1671,1674,1685,1689,1692,1695,1715,1719,1722,1727,1731,1734,1739,1743,1746,1757,1759,1762,1766,1777,1781,1792,1809,1811,1814,1957,1959,1962,1967,1971,1976,1991,1996,2011,2013,2016,2025,2047,2049,2052,2054,2058,2063,2066,2098,2101,2166,2256,2260,2339,2342,2381,2384,2403,2406,2408,2412,2417,2421,2424,2449,2452,2456,2459,2462,2464,2468,2471,2474,2476,2480,2483,2486,2488,2492,2494,2498,2500],[85,995,997],{"id":996},"ㄧfunction","ㄧ、Function",[20,999,1001],{"id":1000},"什麼是function函式","什麼是Function（函式）？",[11,1003,1004],{},"硬體底層的運算軌道裡，滿足以下三者才能被稱作Function",[122,1006,1007,1010,1013],{},[125,1008,1009],{},"有輸入（參數）。",[125,1011,1012],{},"有內部邏輯加工：負責執行特定的運算或程式碼邏輯。",[125,1014,1015],{},"有輸出（回傳值）：它執行了 return。",[28,1017,1019],{"className":30,"code":1018,"language":32,"meta":33,"style":33},"static string GetChicken() { return \"孤獨一隻雞\"; }\n",[35,1020,1021],{"__ignoreMap":33},[38,1022,1023],{"class":40,"line":41},[38,1024,1018],{},[11,1026,1027],{},"雖然因為它住進了 class Program 大宅裡，而在口頭上稱呼它為 「Method（方法）」，但它本質上就是一尊活生生的 Function。",[11,1029,1030],{},"=================",[20,1032,1034],{"id":1033},"function函式與method方法","Function（函式）與Method（方法）",[89,1036,1037],{},[11,1038,1039],{},"在 C# 的世界裡，所有的方法（Method）本質上都是函式（Function），但只有「寄生在類別或結構肚子裡」的函式，才有資格被尊稱為方法。",[608,1041,1043],{"id":1042},"可以用變數的物理居住地來劃分","可以用變數的「物理居住地」來劃分",[122,1045,1046],{},[125,1047,1048,1049],{},"函式 (Function) ➔ 自由流浪的頭等公民\n",[122,1050,1051,1054],{},[125,1052,1053],{},"物理定義：它是獨立存在的。它不屬於任何人，不需要依附在任何 class 或 struct 大宅裡。你只要呼叫它的名字，它就能當場點火通電。",[125,1055,1056],{},"前端 JS 視角：你在 JavaScript 檔案頂層隨手寫的 function，就是標準的函式。",[28,1058,1062],{"className":1059,"code":1060,"language":1061,"meta":33,"style":33},"language-javascript! shiki shiki-themes github-light github-dark","\u002F\u002F 這是純度 100% 的 Function，它是自由的\nfunction sayHello() {\n    console.log(\"Hello\");\n}\n\n","javascript!",[35,1063,1064,1069,1074,1079],{"__ignoreMap":33},[38,1065,1066],{"class":40,"line":41},[38,1067,1068],{},"\u002F\u002F 這是純度 100% 的 Function，它是自由的\n",[38,1070,1071],{"class":40,"line":47},[38,1072,1073],{},"function sayHello() {\n",[38,1075,1076],{"class":40,"line":62},[38,1077,1078],{},"    console.log(\"Hello\");\n",[38,1080,1081],{"class":40,"line":173},[38,1082,270],{},[122,1084,1085],{},[125,1086,1087,1088],{},"方法 (Method) ➔ 寄生在物件大宅裡的「附屬器官」\n",[122,1089,1090],{},[125,1091,1092],{},"物理定義：它是物件導向（OOP）宇宙下的產物。它被迫必須寫在 class 或 struct 的肚子裡。它沒辦法單獨存活，外人想要對它通電，必須先呼叫它主人的名字（物件實例）。",[28,1094,1096],{"className":30,"code":1095,"language":32,"meta":33,"style":33},"public class Robot \n{\n    \u002F\u002F 這是 Method（方法）！因為它寄生在 Robot 大宅裡\n    \u002F\u002F 外面不能直接呼叫 Walk()，必須先 new Robot().Walk()\n    public void Walk() \n    {\n        Console.WriteLine(\"機器人走路\");\n    }\n}\n",[35,1097,1098,1103,1107,1112,1117,1122,1126,1131,1135],{"__ignoreMap":33},[38,1099,1100],{"class":40,"line":41},[38,1101,1102],{},"public class Robot \n",[38,1104,1105],{"class":40,"line":47},[38,1106,165],{},[38,1108,1109],{"class":40,"line":62},[38,1110,1111],{},"    \u002F\u002F 這是 Method（方法）！因為它寄生在 Robot 大宅裡\n",[38,1113,1114],{"class":40,"line":173},[38,1115,1116],{},"    \u002F\u002F 外面不能直接呼叫 Walk()，必須先 new Robot().Walk()\n",[38,1118,1119],{"class":40,"line":179},[38,1120,1121],{},"    public void Walk() \n",[38,1123,1124],{"class":40,"line":185},[38,1125,176],{},[38,1127,1128],{"class":40,"line":191},[38,1129,1130],{},"        Console.WriteLine(\"機器人走路\");\n",[38,1132,1133],{"class":40,"line":197},[38,1134,264],{},[38,1136,1137],{"class":40,"line":203},[38,1138,270],{},[11,1140,1030],{},[20,1142,1144],{"id":1143},"關於function","關於Function",[89,1146,1147],{},[11,1148,1149],{},"C# 的宇宙裡，任何 function 都不准單獨在外面流浪",[122,1151,1152],{},[125,1153,1154,1155],{},"Func 就是 C# 的 Function 變數型別\n",[122,1156,1157,1160],{},[125,1158,1159],{},"如果你的箭頭函式有 return 回傳值 ➔ 它的型別叫做 Func",[125,1161,1162],{},"如果你的箭頭函式純粹做事情，不 return 東西（void） ➔ 它的型別叫做 Action",[28,1164,1166],{"className":30,"code":1165,"language":32,"meta":33,"style":33},"using System;\n\nnamespace CharliesCSharpDemo;\n\n\u002F\u002F 1. 宣告一個極輕量、住 Stack 的 struct\npublic struct OrderProcessor\n{\n    public string OrderId { get; set; }\n    public double TotalAmount { get; set; }\n\n    \u002F\u002F 🚀 亮點：在 struct 肚子裡裝一尊自由的 Func 變數（吃 double，回傳 double）\n    public Func\u003Cdouble, double> DiscountStrategy { get; set; }\n\n    \u002F\u002F 2. 一個普通 Method，在內部點火執行那一尊外來的 Func\n    public readonly double CalculateFinalPrice()\n    {\n        if (DiscountStrategy == null) return TotalAmount;\n        \n        \u002F\u002F 物理通電：把金額餵給那個動態傳進來的箭頭函式\n        return DiscountStrategy(TotalAmount); \n    }\n}\n\n",[35,1167,1168,1173,1177,1182,1186,1191,1196,1200,1205,1210,1214,1219,1224,1228,1233,1238,1242,1247,1252,1257,1262,1267],{"__ignoreMap":33},[38,1169,1170],{"class":40,"line":41},[38,1171,1172],{},"using System;\n",[38,1174,1175],{"class":40,"line":47},[38,1176,194],{"emptyLinePlaceholder":70},[38,1178,1179],{"class":40,"line":62},[38,1180,1181],{},"namespace CharliesCSharpDemo;\n",[38,1183,1184],{"class":40,"line":173},[38,1185,194],{"emptyLinePlaceholder":70},[38,1187,1188],{"class":40,"line":179},[38,1189,1190],{},"\u002F\u002F 1. 宣告一個極輕量、住 Stack 的 struct\n",[38,1192,1193],{"class":40,"line":185},[38,1194,1195],{},"public struct OrderProcessor\n",[38,1197,1198],{"class":40,"line":191},[38,1199,165],{},[38,1201,1202],{"class":40,"line":197},[38,1203,1204],{},"    public string OrderId { get; set; }\n",[38,1206,1207],{"class":40,"line":203},[38,1208,1209],{},"    public double TotalAmount { get; set; }\n",[38,1211,1212],{"class":40,"line":209},[38,1213,194],{"emptyLinePlaceholder":70},[38,1215,1216],{"class":40,"line":215},[38,1217,1218],{},"    \u002F\u002F 🚀 亮點：在 struct 肚子裡裝一尊自由的 Func 變數（吃 double，回傳 double）\n",[38,1220,1221],{"class":40,"line":221},[38,1222,1223],{},"    public Func\u003Cdouble, double> DiscountStrategy { get; set; }\n",[38,1225,1226],{"class":40,"line":227},[38,1227,194],{"emptyLinePlaceholder":70},[38,1229,1230],{"class":40,"line":232},[38,1231,1232],{},"    \u002F\u002F 2. 一個普通 Method，在內部點火執行那一尊外來的 Func\n",[38,1234,1235],{"class":40,"line":238},[38,1236,1237],{},"    public readonly double CalculateFinalPrice()\n",[38,1239,1240],{"class":40,"line":244},[38,1241,176],{},[38,1243,1244],{"class":40,"line":249},[38,1245,1246],{},"        if (DiscountStrategy == null) return TotalAmount;\n",[38,1248,1249],{"class":40,"line":255},[38,1250,1251],{},"        \n",[38,1253,1254],{"class":40,"line":261},[38,1255,1256],{},"        \u002F\u002F 物理通電：把金額餵給那個動態傳進來的箭頭函式\n",[38,1258,1259],{"class":40,"line":267},[38,1260,1261],{},"        return DiscountStrategy(TotalAmount); \n",[38,1263,1265],{"class":40,"line":1264},21,[38,1266,264],{},[38,1268,1270],{"class":40,"line":1269},22,[38,1271,270],{},[608,1273,1275],{"id":1274},"get-set-到底是什麼","{ get; set; } 到底是什麼？",[122,1277,1278],{},[125,1279,1280],{},"標準的物件導向寫法",[28,1282,1284],{"className":30,"code":1283,"language":32,"meta":33,"style":33},"private string _orderId; \u002F\u002F 物理底層私有欄位\n\npublic string GetOrderId() { return _orderId; } \u002F\u002F 讀取門衛\npublic void SetOrderId(string value) { _orderId = value; } \u002F\u002F 寫入門衛\n",[35,1285,1286,1291,1295,1300],{"__ignoreMap":33},[38,1287,1288],{"class":40,"line":41},[38,1289,1290],{},"private string _orderId; \u002F\u002F 物理底層私有欄位\n",[38,1292,1293],{"class":40,"line":47},[38,1294,194],{"emptyLinePlaceholder":70},[38,1296,1297],{"class":40,"line":62},[38,1298,1299],{},"public string GetOrderId() { return _orderId; } \u002F\u002F 讀取門衛\n",[38,1301,1302],{"class":40,"line":173},[38,1303,1304],{},"public void SetOrderId(string value) { _orderId = value; } \u002F\u002F 寫入門衛\n",[122,1306,1307],{},[125,1308,1309],{},"屬性語法糖",[28,1311,1313],{"className":30,"code":1312,"language":32,"meta":33,"style":33},"public string OrderId { get; set; }\n",[35,1314,1315],{"__ignoreMap":33},[38,1316,1317],{"class":40,"line":41},[38,1318,1312],{},[608,1320,1322],{"id":1321},"func-到底算是方法-method還是函式-function","Func 到底算是「方法 (Method)」還是「函式 (Function)」？",[89,1324,1325],{},[11,1326,1327],{},"在 C# 裡，Func 在「行為」上是 Function（函式），但在「底層物理型別」上，它是一個物件（Object）！",[89,1329,1330],{},[11,1331,1332],{},"Func 算是一個變數\u002F物件",[11,1334,1335],{},"+++++++++",[11,1337,1338],{},"Action 沒有回傳值算是Function嗎？",[89,1340,1341],{},[11,1342,1343],{},"Func 和 Action 物理地位完全平起平坐，兩個都必須算成是 Function 的一種！",[11,1345,1346],{},[1347,1348,1349],"em",{},[15,1350,1351],{},"其實 Action 在底層「也有 Return」=> Void 函式",[11,1353,1335],{},[122,1355,1356],{},[125,1357,1358,1359],{},"總結：\n",[122,1360,1361,1364],{},[125,1362,1363],{},"C# 的 { get; set; } ➔ 就是讓變數變成外人可以讀寫的標準屬性。",[125,1365,1366],{},"C# 的 Func ➔ 就是 JS 裡的 Function 變數欄位（可以裝箭頭函式的容器）。",[11,1368,1369],{},"====================",[20,1371,1372],{"id":1372},"函式語法結構定義",[28,1374,1376],{"className":30,"code":1375,"language":32,"meta":33,"style":33},"[修飾詞] [回傳型別] [方法名稱]([參數型別] [參數名稱])\n",[35,1377,1378],{"__ignoreMap":33},[38,1379,1380],{"class":40,"line":41},[38,1381,1375],{},[11,1383,1384],{},"看看一般的基本型別寫法",[28,1386,1388],{"className":30,"code":1387,"language":32,"meta":33,"style":33},"public static int CalculateAge(string birthDate)\n",[35,1389,1390],{"__ignoreMap":33},[38,1391,1392],{"class":40,"line":41},[38,1393,1387],{},[89,1395,1396],{},[11,1397,1398,1399,1402,1403,142],{},"此處 ",[35,1400,1401],{},"int"," 後面不會有變數名稱，因為它代表的是",[15,1404,1405],{},"輸出型別",[28,1407,1409],{"className":30,"code":1408,"language":32,"meta":33,"style":33},"[回傳型別] [方法名稱] ( [參數型別_1] [參數名稱_1], [參數型別_2] [參數名稱_2], ... )\n",[35,1410,1411],{"__ignoreMap":33},[38,1412,1413],{"class":40,"line":41},[38,1414,1408],{},[89,1416,1417],{},[11,1418,1419],{},"參數「必須有名稱」才能在方法內被使用",[272,1421],{},[85,1423,1425],{"id":1424},"二console控制台專案和-web-api網頁專案","二、Console（控制台專案）和 Web API（網頁專案）",[20,1427,1429],{"id":1428},"console-專案單純跑完即死的打工仔","Console 專案：單純、跑完即死的「打工仔」",[122,1431,1432,1435],{},[125,1433,1434],{},"微軟預載的依賴：極度乾淨、輕量。它底層只認最基础的資料結構（如 int、string、List），完全沒有處理網路 HTTP 請求、路由、安全性驗證、JSON 序列化的能力。",[125,1436,1437],{},"使命：\n-    練習 LeetCode 演算法、測試 C# 新語法\n-    寫排程工具（Cron Job）",[11,1439,1440],{},"==================",[20,1442,1444],{"id":1443},"web-api-專案永不熄燈瘋狂接客的鋼鐵飯店","Web API 專案：永不熄燈、瘋狂接客的「鋼鐵飯店」",[122,1446,1447,1450],{},[125,1448,1449],{},"生存邏輯：「死循環監聽（Infinite Loop）」。",[125,1451,1452,1453],{},"在建立專案時，就幫你強行灌入了一整套龐大的 ASP.NET Core 框架網頁伺服器（Kestrel）\n",[122,1454,1455,1458,1461],{},[125,1456,1457],{},"前端傳過來的 JSON 怎麼自動轉成 C# 物件？",[125,1459,1460],{},"網址路由 \u002Fapi\u002Fproduct\u002F123 該分配給哪一個 Controller（控制器）去加工？",[125,1462,1463],{},"怎麼跟前端的 Next.js 跨網域（CORS）通訊？",[272,1465],{},[85,1467,1469],{"id":1468},"三-get-set","三、{ get; set; }",[20,1471,1473],{"id":1472},"是什麼","是什麼？",[387,1475,1476,1479],{},[125,1477,1478],{},"屬性 (Property)」",[125,1480,1481],{},"物理本質是微軟幫你自動生成的「小守門員方法（Method）」",[20,1483,1485],{"id":1484},"存在何處","存在何處？",[387,1487,1488,1491,1494],{},[125,1489,1490],{},"{ get; set; } 不是看記憶體位置（Heap\u002FStack）決定的，而是看「階級身分」決定的。",[125,1492,1493],{},"只要它是屬於 class 或 struct 的「大樓成員（屬性）」，它就有資格配戴 { get; set; } 這對守門員佩刀。",[125,1495,1496],{},"如果它只是方法肚子裡的「臨時工（區域變數）」，就絕對不能加 { get; set; }。",[20,1498,1500],{"id":1499},"為什麼","為什麼？",[11,1502,1503],{},"只有「外層的成員」有資格擁有 { get; set; }，是因為它們的生命夠長、且身負「跟外面世界通訊」的職責。",[11,1505,1506],{},"方法肚子裡的變數，只是為了幫外面那些尊貴的成員做短暫代工的「數字打工仔」，所以只配光溜溜地躺在 Stack 裡，不配穿上 { get; set; } 這套制服。",[20,1508,1509],{"id":1509},"init",[89,1511,1512],{},[11,1513,1514],{},"在 init 誕生以前，微軟面臨了一個兩難的軟體工程大悖論：",[122,1516,1517,1520],{},[125,1518,1519],{},"如果用 { get; set; }：\n外面點火很爽，可以用大括號自由挑選參數 new ConnectionOptions { Host = \"db.com\" }。但缺點是，這棟大樓蓋好後，外面任何人都可以在幾天後隨時跑來改它（例如 options.Host = \"hacker.com\"）。這在需要「高安全性、不希望配置被亂改」的 Web API 專案裡，是個巨大的不安全地雷。",[125,1521,1522],{},"如果用 { get; } + 建構子（唯讀）：\n雖然安全（出生後就鎖死），但你就被迫必須在 class 肚子裡寫一堆又臭又長的建構子電線，外面的人 new 時還得嚴格遵守參數順序，失去了大括號物件初始化器的自由度。",[20,1524,1526],{"id":1525},"set-vs-init-vs-readonly-終極大對齊","set vs init vs readonly 終極大對齊",[1528,1529,1530,1561],"table",{},[1531,1532,1533],"thead",{},[1534,1535,1536,1541,1544,1551,1558],"tr",{},[1537,1538,1540],"th",{"align":1539},"left","守門員寫法",[1537,1542,1543],{"align":1539},"允許在「建構子」裡賦值？",[1537,1545,1546,1547,1550],{"align":1539},"允許在「大括號 ",[35,1548,1549],{},"{}"," 初始化器」裡？",[1537,1552,1553,1554,1557],{"align":1539},"物件 ",[35,1555,1556],{},"new"," 完後，允許在外面隨時修改？",[1537,1559,1560],{"align":1539},"它的終極人設",[1562,1563,1564,1588,1616],"tbody",{},[1534,1565,1566,1574,1577,1579,1582],{},[1567,1568,1569],"td",{"align":1539},[15,1570,1571],{},[35,1572,1573],{},"{ get; set; }",[1567,1575,1576],{"align":1539},"🟢 可以",[1567,1578,1576],{"align":1539},[1567,1580,1581],{"align":1539},"🟢 可以（完全自由）",[1567,1583,1584,1587],{"align":1539},[15,1585,1586],{},"開放式住家","：隨時歡迎你來改。",[1534,1589,1590,1597,1599,1602,1605],{},[1567,1591,1592],{"align":1539},[15,1593,1594],{},[35,1595,1596],{},"{ get; init; }",[1567,1598,1576],{"align":1539},[1567,1600,1601],{"align":1539},"🟢 可以（這就是它的超能力！）",[1567,1603,1604],{"align":1539},"❌ 絕對不行（當場鎖死）",[1567,1606,1607,336,1610,1612,1613,1615],{"align":1539},[15,1608,1609],{},"新創安全鎖",[35,1611,1556],{}," 的時候讓你填，",[35,1614,1556],{}," 完立刻灌漿封死。",[1534,1617,1618,1625,1627,1630,1632],{},[1567,1619,1620],{"align":1539},[15,1621,1622],{},[35,1623,1624],{},"{ get; }",[1567,1626,1576],{"align":1539},[1567,1628,1629],{"align":1539},"❌ 絕對不行（編譯器噴錯）",[1567,1631,1604],{"align":1539},[1567,1633,1634,1637],{"align":1539},[15,1635,1636],{},"古代銅牆鐵壁","：只有親生父親（建構子）能填，其他人碰都不准碰。",[272,1639],{},[85,1641,1643],{"id":1642},"肆五層通電過濾網","肆、五層通電過濾網",[20,1645,1646],{"id":1646},"五層簡介",[28,1648,1652],{"className":1649,"code":1650,"language":1651,"meta":33,"style":33},"language-Plaintext! shiki shiki-themes github-light github-dark","［第1層：權限］ ➔ ［第2層：空間與繼承］ ➔ ［第3層：狀態］ ➔ ［第4層：契約］ ➔ ［第5層：特異功能］ ➔ ［型別］ ➔ ［名字］\n","Plaintext!",[35,1653,1654],{"__ignoreMap":33},[38,1655,1656],{"class":40,"line":41},[38,1657,1650],{},[608,1659,1660],{"id":1660},"順序",[11,1662,1663],{},"$$\\text{我有權限看嗎？} \\rightarrow \\text{它在 RAM 的哪張表上？} \\rightarrow \\text{那塊晶片防不防寫？} \\rightarrow \\text{初始化有沒有少材料？} \\rightarrow \\text{CPU 該用什麼模式跑？}$$",[11,1665,1666],{},"權限 ➔ 記憶體地址 ➔ 晶片狀態 ➔ 初始化 ➔ CPU 運作",[608,1668,1670],{"id":1669},"第一層權限濾網誰能調用我-決定人際關係","🔴 第一層：權限濾網（誰能調用我？）➔ 決定「人際關係」",[11,1672,1673],{},"這層站在最前面，第一時間決定外面的路人、隔壁的專案、或是自己的兒子能不能摸到這條電路。",[122,1675,1676,1679,1682],{},[125,1677,1678],{},"public（大老二流派）：全宇宙、全專案的任何人（包含你的 Next.js 前端 API 路由）都可以公開存取。",[125,1680,1681],{},"private（邊緣人流派）：只有這個 class\u002Fstruct 自己肚子裡的代碼才能用，外面的人連看都看不到（例如私有欄位 _orderId 門衛）。",[125,1683,1684],{},"protected（家族繼承流派）：只有自己、以及親生兒子（子類別）才能通電，外面的鄰居路人一律不給碰。",[608,1686,1688],{"id":1687},"第二層空間與繼承濾網我怎麼存在子孫能改嗎-決定物理定位與多型基因","🔵 第二層：空間與繼承濾網（我怎麼存在？子孫能改嗎？）➔ 決定「物理定位與多型基因」",[11,1690,1691],{},"⚠️ 核心鐵律：這一層的所有關鍵字，在 C# 語法裡是「互斥」的！它們佔用同一個生態位，你只能多選一，絕對不可能同時出現！",[11,1693,1694],{},"沒有寫（一般實例成員）：它是附屬器官。外面的人一定要先 new 出一個肉身實體大樓，才能透過點（.）來調用。",[122,1696,1697,1700,1703,1706,1709,1712],{},[125,1698,1699],{},"static（靜態成員）：免 new 的「大樓公共廁所」！一開車就物理性黏在記憶體核心 VIP 區，全宇宙共享唯一一份。",[125,1701,1702],{},"virtual（虛擬基因）：老爸說：「這是一個原廠音響，我寫了基礎功能，但我允許兒子未來可以把它拆掉改裝（可覆寫）。」",[125,1704,1705],{},"override（覆寫基因）：兒子說：「我現在正拿著工具把老爸的舊音響拆了，強行換上我的重低音藍牙音響（覆寫完成）。」",[125,1707,1708],{},"abstract（抽象契約）：老爸只在牆上畫了一個音響的線條框框，肚子裡沒晶片，轉頭咆哮：「你生出來時必須自己實作出來，不然不准開機！」",[125,1710,1711],{},"sealed（封印密封）：大兒子改裝完音響後，拿電焊把外殼死死焊住：「到我為止！接下來的曾孫輩不准再改這台音響了！」",[125,1713,1714],{},"new（血緣隱藏）：兒子想裝新音響，但不想拆老爸的，直接在旁邊偷偷加掛一台，假裝老爸那台不存在。",[608,1716,1718],{"id":1717},"第三層狀態濾網能不能改我-決定肉身能不能變更","🟢 第三層：狀態濾網（能不能改我？）➔ 決定「肉身能不能變更」",[11,1720,1721],{},"這層決定了這塊記憶體格子被填入資料後，電路開關會不會被硬性鎖死。",[122,1723,1724],{},[125,1725,1726],{},"readonly（唯讀）：只能在出生（建構子）那一秒被設定一次，之後直接灌漿鎖死，只能讀取（get），不能寫入（set）。(註：如果是屬性 Property，現代 C# 通常會用 init; 在後方大括號結合控制)。",[608,1728,1730],{"id":1729},"第四層契約濾網有沒有強迫填值-決定初始化的強迫症","🟡 第四層：契約濾網（有沒有強迫填值？）➔ 決定「初始化的強迫症」",[11,1732,1733],{},"這是現代 C# 逼迫外部工程師的硬核防呆契約。",[122,1735,1736],{},[125,1737,1738],{},"required（強迫症契約）：強迫外部工程師在 new 的那一秒，必須用大括號把這條電路的數值交出來（例如：new Order() { OrderId = \"123\" }），漏填連編譯都不會過。",[608,1740,1742],{"id":1741},"第五層特異功能濾網這條電路怎麼跑-決定執行流的外掛","🟣 第五層：特異功能濾網（這條電路怎麼跑？）➔ 決定「執行流的外掛」",[11,1744,1745],{},"這一層決定了這段代碼在被 CPU 執行時，有沒有什麼特殊的「物理時空扭曲」：",[122,1747,1748,1751,1754],{},[125,1749,1750],{},"async（非同步）：時間管理大師。通電時不需要在原地傻傻等它完工，CPU 可以先跳去接別的電路（前端最熟的 async \u002F await）。",[125,1752,1753],{},"unsafe（不安全代碼）：解鎖硬體禁區。允許在這段代碼裡像 C\u002FC++ 一樣，直接操作實體記憶體指針（Pointer），海關（CLR）不再保護你。",[125,1755,1756],{},"extern（外部調用）：這條電路的本體根本不在 C# 肚子裡， expansion 到外部去呼叫 C++ 編譯出來的 .dll 檔案。",[11,1758,1030],{},[20,1760,1761],{"id":1761},"衝突",[608,1763,1765],{"id":1764},"衝突一第二層的-static-第四層的-required","衝突一：第二層的 static ⛔ 第四層的 required",[122,1767,1768,1771,1774],{},[125,1769,1770],{},"物理現實： static（大樓公設）與 required（強迫住戶裝潢）絕對不能同時出現！",[125,1772,1773],{},"原因： required 是現代 C# 為了強迫外部在 new 物件實體時，必須用大括號填入數值。但 static 成員根本不需要 new 就能用，所以系統會當場錯亂。",[125,1775,1776],{},"結論： 只要開了 static 燈號，第四層的 required 燈號就會被強制拔除。",[608,1778,1780],{"id":1779},"衝突二第三層的-readonly-第四層的-required針對欄位-field","衝突二：第三層的 readonly ⛔ 第四層的 required（針對欄位 Field）",[122,1782,1783,1786,1789],{},[125,1784,1785],{},"物理現實： 如果你宣告的是一個「欄位（Field）」，readonly 和 required 不能共存！",[125,1787,1788],{},"原因： readonly 規定變數只能在「建構子內部」出生時賦值；但 required 卻是強迫別人在「建構子外部的物件初始化器 {}」填值，兩者在執行時間線上會打架。",[125,1790,1791],{},"現代 C# 的標準解法（改用屬性 Property）：\n如果你希望一個屬性既是強迫填值、填完後又是唯讀不可修改的，在現代 C# 裡我們不寫 readonly，而是會結合第四層寫成這樣：",[28,1793,1797],{"className":1794,"code":1795,"language":1796,"meta":33,"style":33},"language-csharp! shiki shiki-themes github-light github-dark","\u002F\u002F 完美的現代 C# 組合：權限(1) ➔ 契約(4) ➔ 形態 ➔ 名字 ➔ { get; 狀態(3) }\npublic required string SystemToken { get; init; }\n","csharp!",[35,1798,1799,1804],{"__ignoreMap":33},[38,1800,1801],{"class":40,"line":41},[38,1802,1803],{},"\u002F\u002F 完美的現代 C# 組合：權限(1) ➔ 契約(4) ➔ 形態 ➔ 名字 ➔ { get; 狀態(3) }\n",[38,1805,1806],{"class":40,"line":47},[38,1807,1808],{},"public required string SystemToken { get; init; }\n",[11,1810,1030],{},[20,1812,1813],{"id":1813},"關於權限",[1528,1815,1816,1839],{},[1531,1817,1818],{},[1534,1819,1820,1823,1827,1830,1833,1836],{},[1537,1821,1822],{"align":1539},"權限修飾詞",[1537,1824,1826],{"align":1825},"center","同一個 Class 內",[1537,1828,1829],{"align":1825},"同專案的子類別 (兒子)",[1537,1831,1832],{"align":1825},"同專案的其他 Class (鄰居)",[1537,1834,1835],{"align":1825},"不同專案的子類別 (外地兒子)",[1537,1837,1838],{"align":1825},"不同專案的其他 Class (外星人)",[1562,1840,1841,1862,1881,1900,1919,1938],{},[1534,1842,1843,1850,1853,1856,1858,1860],{},[1567,1844,1845],{"align":1539},[15,1846,1847],{},[35,1848,1849],{},"private",[1567,1851,1852],{"align":1825},"🟢",[1567,1854,1855],{"align":1825},"❌",[1567,1857,1855],{"align":1825},[1567,1859,1855],{"align":1825},[1567,1861,1855],{"align":1825},[1534,1863,1864,1871,1873,1875,1877,1879],{},[1567,1865,1866],{"align":1539},[15,1867,1868],{},[35,1869,1870],{},"private protected",[1567,1872,1852],{"align":1825},[1567,1874,1852],{"align":1825},[1567,1876,1855],{"align":1825},[1567,1878,1855],{"align":1825},[1567,1880,1855],{"align":1825},[1534,1882,1883,1890,1892,1894,1896,1898],{},[1567,1884,1885],{"align":1539},[15,1886,1887],{},[35,1888,1889],{},"protected",[1567,1891,1852],{"align":1825},[1567,1893,1852],{"align":1825},[1567,1895,1855],{"align":1825},[1567,1897,1852],{"align":1825},[1567,1899,1855],{"align":1825},[1534,1901,1902,1909,1911,1913,1915,1917],{},[1567,1903,1904],{"align":1539},[15,1905,1906],{},[35,1907,1908],{},"internal",[1567,1910,1852],{"align":1825},[1567,1912,1852],{"align":1825},[1567,1914,1852],{"align":1825},[1567,1916,1855],{"align":1825},[1567,1918,1855],{"align":1825},[1534,1920,1921,1928,1930,1932,1934,1936],{},[1567,1922,1923],{"align":1539},[15,1924,1925],{},[35,1926,1927],{},"protected internal",[1567,1929,1852],{"align":1825},[1567,1931,1852],{"align":1825},[1567,1933,1852],{"align":1825},[1567,1935,1852],{"align":1825},[1567,1937,1855],{"align":1825},[1534,1939,1940,1947,1949,1951,1953,1955],{},[1567,1941,1942],{"align":1539},[15,1943,1944],{},[35,1945,1946],{},"public",[1567,1948,1852],{"align":1825},[1567,1950,1852],{"align":1825},[1567,1952,1852],{"align":1825},[1567,1954,1852],{"align":1825},[1567,1956,1852],{"align":1825},[11,1958,1030],{},[20,1960,1961],{"id":1961},"關於static",[89,1963,1964],{},[11,1965,1966],{},"static 的物理本質就是「記憶體上的共用公設」！它不需要 new，因為它在程式一開車時就已經物理性地黏在記憶體核心裡了。",[608,1968,1970],{"id":1969},"用大樓公設一秒看穿-static","用「大樓公設」一秒看穿 static",[387,1972,1973],{},[125,1974,1975],{},"沒有寫 static（實例成員）➔ 它是「住戶家裡的個人馬桶」",[28,1977,1979],{"className":30,"code":1978,"language":32,"meta":33,"style":33},"var roomA = new ConnectionSettings();\nroomA.Port = 8080; \u002F\u002F 這是 roomA 家的馬桶，跟 roomB 沒關係\n",[35,1980,1981,1986],{"__ignoreMap":33},[38,1982,1983],{"class":40,"line":41},[38,1984,1985],{},"var roomA = new ConnectionSettings();\n",[38,1987,1988],{"class":40,"line":47},[38,1989,1990],{},"roomA.Port = 8080; \u002F\u002F 這是 roomA 家的馬桶，跟 roomB 沒關係\n",[387,1992,1993],{"start":47},[125,1994,1995],{},"加上了 static（靜態成員）➔ 它是「大樓大廳的公共廁所」",[28,1997,1999],{"className":30,"code":1998,"language":32,"meta":33,"style":33},"\u002F\u002F 🚀 看好！完全不需要 new Console()！直接呼叫大樓名字 Console 就能點火！\nConsole.WriteLine(\"我是大樓公廁，誰都可以直接用我！\");\n",[35,2000,2001,2006],{"__ignoreMap":33},[38,2002,2003],{"class":40,"line":41},[38,2004,2005],{},"\u002F\u002F 🚀 看好！完全不需要 new Console()！直接呼叫大樓名字 Console 就能點火！\n",[38,2007,2008],{"class":40,"line":47},[38,2009,2010],{},"Console.WriteLine(\"我是大樓公廁，誰都可以直接用我！\");\n",[11,2012,1030],{},[20,2014,2015],{"id":2015},"舉例",[28,2017,2019],{"className":30,"code":2018,"language":32,"meta":33,"style":33},"public static readonly double Pi = 3.14159;\n",[35,2020,2021],{"__ignoreMap":33},[38,2022,2023],{"class":40,"line":41},[38,2024,2018],{},[28,2026,2030],{"className":2027,"code":2028,"language":2029,"meta":33,"style":33},"language-Plaintext＝ shiki shiki-themes github-light github-dark","［第一層：權限］ ➔ ［第二層：空間］ ➔ ［第三層：狀態］ ➔ ［型別］ ➔ ［名字］\n    public          static         readonly       double      Pi = 3.14;\n  (全宇宙可看)     (大樓公設免new)  (灌漿鎖死不能改)\n","Plaintext＝",[35,2031,2032,2037,2042],{"__ignoreMap":33},[38,2033,2034],{"class":40,"line":41},[38,2035,2036],{},"［第一層：權限］ ➔ ［第二層：空間］ ➔ ［第三層：狀態］ ➔ ［型別］ ➔ ［名字］\n",[38,2038,2039],{"class":40,"line":47},[38,2040,2041],{},"    public          static         readonly       double      Pi = 3.14;\n",[38,2043,2044],{"class":40,"line":62},[38,2045,2046],{},"  (全宇宙可看)     (大樓公設免new)  (灌漿鎖死不能改)\n",[11,2048,1030],{},[20,2050,2051],{"id":2051},"補充",[272,2053],{},[85,2055,2057],{"id":2056},"五建構子","五、建構子",[89,2059,2060],{},[11,2061,2062],{},"建構子不是必要的：你完全可以不寫。只要不寫，類別就會自帶一個隱形的「無參數刷零建構子」。\n它的名字有沒有跟外側的 class 或 struct 一模一樣",[20,2064,2065],{"id":2065},"class",[28,2067,2069],{"className":30,"code":2068,"language":32,"meta":33,"style":33},"public class Customer\n{\n    public string Name { get; set; }\n\n    public Customer(string name) => Name = name;\n}\n",[35,2070,2071,2076,2080,2085,2089,2094],{"__ignoreMap":33},[38,2072,2073],{"class":40,"line":41},[38,2074,2075],{},"public class Customer\n",[38,2077,2078],{"class":40,"line":47},[38,2079,165],{},[38,2081,2082],{"class":40,"line":62},[38,2083,2084],{},"    public string Name { get; set; }\n",[38,2086,2087],{"class":40,"line":173},[38,2088,194],{"emptyLinePlaceholder":70},[38,2090,2091],{"class":40,"line":179},[38,2092,2093],{},"    public Customer(string name) => Name = name;\n",[38,2095,2096],{"class":40,"line":185},[38,2097,270],{},[20,2099,2100],{"id":2100},"struct",[28,2102,2104],{"className":30,"code":2103,"language":32,"meta":33,"style":33},"struct ConnectionSettings\n{\n    public string Host { get; set; }\n    public int Port { get; set; }\n    public int MaxRetries { get; set; }\n\n    public ConnectionSettings()\n    {\n        Host = \"localhost\";\n        Port = 8080;\n        MaxRetries = 3;\n    }\n}\n",[35,2105,2106,2111,2115,2120,2125,2130,2134,2139,2143,2148,2153,2158,2162],{"__ignoreMap":33},[38,2107,2108],{"class":40,"line":41},[38,2109,2110],{},"struct ConnectionSettings\n",[38,2112,2113],{"class":40,"line":47},[38,2114,165],{},[38,2116,2117],{"class":40,"line":62},[38,2118,2119],{},"    public string Host { get; set; }\n",[38,2121,2122],{"class":40,"line":173},[38,2123,2124],{},"    public int Port { get; set; }\n",[38,2126,2127],{"class":40,"line":179},[38,2128,2129],{},"    public int MaxRetries { get; set; }\n",[38,2131,2132],{"class":40,"line":185},[38,2133,194],{"emptyLinePlaceholder":70},[38,2135,2136],{"class":40,"line":191},[38,2137,2138],{},"    public ConnectionSettings()\n",[38,2140,2141],{"class":40,"line":197},[38,2142,176],{},[38,2144,2145],{"class":40,"line":203},[38,2146,2147],{},"        Host = \"localhost\";\n",[38,2149,2150],{"class":40,"line":209},[38,2151,2152],{},"        Port = 8080;\n",[38,2154,2155],{"class":40,"line":215},[38,2156,2157],{},"        MaxRetries = 3;\n",[38,2159,2160],{"class":40,"line":221},[38,2161,264],{},[38,2163,2164],{"class":40,"line":227},[38,2165,270],{},[28,2167,2169],{"className":30,"code":2168,"language":32,"meta":33,"style":33},"public class Robot\n{\n    public string Model { get; set; }\n\n    \u002F\u002F 👑 尊貴建構子 A（正統流派）\n    \u002F\u002F 特徵：名字叫 Robot (跟外側 class 同名)，而且前面「完全沒有回傳型別」！\n    public Robot(string model) => Model = model;\n\n    \u002F\u002F ❌ 普通方法 B（魚目混珠流派）\n    \u002F\u002F 特徵：長得一模一樣，但前面加了 \"void\" (代表它會回傳空值)！\n    \u002F\u002F 物理真相：編譯器大喊：「你雖然叫 Robot，但你宣告了回傳型別 void！你只是個剛好跟大樓同名的普通方法，不准當建構子！」\n    public void Robot(string model) => Model = model;\n\n    \u002F\u002F ❌ 普通方法 C（路人甲流派）\n    \u002F\u002F 特徵：名字叫 Initialize，跟 class 不同名。\n    \u002F\u002F 物理真相：這就是個純度 100% 的普通方法。\n    public void Initialize(string model) => Model = model;\n}\n",[35,2170,2171,2176,2180,2185,2189,2194,2199,2204,2208,2213,2218,2223,2228,2232,2237,2242,2247,2252],{"__ignoreMap":33},[38,2172,2173],{"class":40,"line":41},[38,2174,2175],{},"public class Robot\n",[38,2177,2178],{"class":40,"line":47},[38,2179,165],{},[38,2181,2182],{"class":40,"line":62},[38,2183,2184],{},"    public string Model { get; set; }\n",[38,2186,2187],{"class":40,"line":173},[38,2188,194],{"emptyLinePlaceholder":70},[38,2190,2191],{"class":40,"line":179},[38,2192,2193],{},"    \u002F\u002F 👑 尊貴建構子 A（正統流派）\n",[38,2195,2196],{"class":40,"line":185},[38,2197,2198],{},"    \u002F\u002F 特徵：名字叫 Robot (跟外側 class 同名)，而且前面「完全沒有回傳型別」！\n",[38,2200,2201],{"class":40,"line":191},[38,2202,2203],{},"    public Robot(string model) => Model = model;\n",[38,2205,2206],{"class":40,"line":197},[38,2207,194],{"emptyLinePlaceholder":70},[38,2209,2210],{"class":40,"line":203},[38,2211,2212],{},"    \u002F\u002F ❌ 普通方法 B（魚目混珠流派）\n",[38,2214,2215],{"class":40,"line":209},[38,2216,2217],{},"    \u002F\u002F 特徵：長得一模一樣，但前面加了 \"void\" (代表它會回傳空值)！\n",[38,2219,2220],{"class":40,"line":215},[38,2221,2222],{},"    \u002F\u002F 物理真相：編譯器大喊：「你雖然叫 Robot，但你宣告了回傳型別 void！你只是個剛好跟大樓同名的普通方法，不准當建構子！」\n",[38,2224,2225],{"class":40,"line":221},[38,2226,2227],{},"    public void Robot(string model) => Model = model;\n",[38,2229,2230],{"class":40,"line":227},[38,2231,194],{"emptyLinePlaceholder":70},[38,2233,2234],{"class":40,"line":232},[38,2235,2236],{},"    \u002F\u002F ❌ 普通方法 C（路人甲流派）\n",[38,2238,2239],{"class":40,"line":238},[38,2240,2241],{},"    \u002F\u002F 特徵：名字叫 Initialize，跟 class 不同名。\n",[38,2243,2244],{"class":40,"line":244},[38,2245,2246],{},"    \u002F\u002F 物理真相：這就是個純度 100% 的普通方法。\n",[38,2248,2249],{"class":40,"line":249},[38,2250,2251],{},"    public void Initialize(string model) => Model = model;\n",[38,2253,2254],{"class":40,"line":255},[38,2255,270],{},[20,2257,2259],{"id":2258},"類別就會自帶一個隱形的無參數刷零建構子","類別就會自帶一個隱形的「無參數刷零建構子」",[28,2261,2263],{"className":30,"code":2262,"language":32,"meta":33,"style":33},"\u002F\u002F 假設這是你寫的，沒有任何的建構子\npublic class Member\n{\n    public string Account { get; set; }\n    public string Email { get; set; }\n    public int Age { get; set; }\n}\n\n\u002F\u002F------------------------\n\n\u002F\u002F 🟢 微軟一輩子只會幫你自動補上這個「全空殼」：\npublic Member()\n{\n    \u002F\u002F 裡面是空的！一句話都不說！\n    \u002F\u002F 讓你的 Account 變 null，Email 變 null，Age 變 0\n}\n\n",[35,2264,2265,2270,2275,2279,2284,2289,2294,2298,2302,2307,2311,2316,2321,2325,2330,2335],{"__ignoreMap":33},[38,2266,2267],{"class":40,"line":41},[38,2268,2269],{},"\u002F\u002F 假設這是你寫的，沒有任何的建構子\n",[38,2271,2272],{"class":40,"line":47},[38,2273,2274],{},"public class Member\n",[38,2276,2277],{"class":40,"line":62},[38,2278,165],{},[38,2280,2281],{"class":40,"line":173},[38,2282,2283],{},"    public string Account { get; set; }\n",[38,2285,2286],{"class":40,"line":179},[38,2287,2288],{},"    public string Email { get; set; }\n",[38,2290,2291],{"class":40,"line":185},[38,2292,2293],{},"    public int Age { get; set; }\n",[38,2295,2296],{"class":40,"line":191},[38,2297,270],{},[38,2299,2300],{"class":40,"line":197},[38,2301,194],{"emptyLinePlaceholder":70},[38,2303,2304],{"class":40,"line":203},[38,2305,2306],{},"\u002F\u002F------------------------\n",[38,2308,2309],{"class":40,"line":209},[38,2310,194],{"emptyLinePlaceholder":70},[38,2312,2313],{"class":40,"line":215},[38,2314,2315],{},"\u002F\u002F 🟢 微軟一輩子只會幫你自動補上這個「全空殼」：\n",[38,2317,2318],{"class":40,"line":221},[38,2319,2320],{},"public Member()\n",[38,2322,2323],{"class":40,"line":227},[38,2324,165],{},[38,2326,2327],{"class":40,"line":232},[38,2328,2329],{},"    \u002F\u002F 裡面是空的！一句話都不說！\n",[38,2331,2332],{"class":40,"line":238},[38,2333,2334],{},"    \u002F\u002F 讓你的 Account 變 null，Email 變 null，Age 變 0\n",[38,2336,2337],{"class":40,"line":244},[38,2338,270],{},[11,2340,2341],{},"變成這樣",[28,2343,2345],{"className":30,"code":2344,"language":32,"meta":33,"style":33},"public class Member\n{\n    public string Account { get; set; }\n    public string Email { get; set; }\n    public int Age { get; set; }\n    \n    public Member(){}    \n}\n\n",[35,2346,2347,2351,2355,2359,2363,2367,2372,2377],{"__ignoreMap":33},[38,2348,2349],{"class":40,"line":41},[38,2350,2274],{},[38,2352,2353],{"class":40,"line":47},[38,2354,165],{},[38,2356,2357],{"class":40,"line":62},[38,2358,2283],{},[38,2360,2361],{"class":40,"line":173},[38,2362,2288],{},[38,2364,2365],{"class":40,"line":179},[38,2366,2293],{},[38,2368,2369],{"class":40,"line":185},[38,2370,2371],{},"    \n",[38,2373,2374],{"class":40,"line":191},[38,2375,2376],{},"    public Member(){}    \n",[38,2378,2379],{"class":40,"line":197},[38,2380,270],{},[11,2382,2383],{},"如果要使用的話",[28,2385,2387],{"className":30,"code":2386,"language":32,"meta":33,"style":33},"var a = new Member() { Account = \"charlie\", Email = \"charlie's mail\" };\n\n### 沒有Age的話，會帶入初始值0\n",[35,2388,2389,2394,2398],{"__ignoreMap":33},[38,2390,2391],{"class":40,"line":41},[38,2392,2393],{},"var a = new Member() { Account = \"charlie\", Email = \"charlie's mail\" };\n",[38,2395,2396],{"class":40,"line":47},[38,2397,194],{"emptyLinePlaceholder":70},[38,2399,2400],{"class":40,"line":62},[38,2401,2402],{},"### 沒有Age的話，會帶入初始值0\n",[20,2404,2405],{"id":830},"寫法：",[272,2407],{},[85,2409,2411],{"id":2410},"六靜態static","六、靜態（Static）",[89,2413,2414],{},[11,2415,2416],{},"在硬體實體記憶體（RAM）裡的「空間與位置」，在程式啟動期間是絕對固定、無法被回收的！",[20,2418,2420],{"id":2419},"記憶體的三種配置宿命","記憶體的三種「配置宿命」",[11,2422,2423],{},"在硬體底層，當你的程式（.exe 或 Web API）被載入到 RAM 執行時，作業系統會把記憶體劃分成三種管理模式。這就是為什麼它們的名字長這樣：",[28,2425,2427],{"className":1649,"code":2426,"language":1651,"meta":33,"style":33},"💾 你的實體記憶體 (RAM)\n├── 🟩 [靜態配置區] ── 啟動時直接「圈地封王」，位置固定，與程式同生共死 (Static)\n├── 🟦 [Stack 棧區]  ── 隨著函式進出，CPU 指針上下跳動，用完秒回收 (Automatic)\n└── 🟨 [Heap 堆區]   ── 程式執行時動態去申請一塊地，不用了派垃圾車回收 (Dynamic)\n",[35,2428,2429,2434,2439,2444],{"__ignoreMap":33},[38,2430,2431],{"class":40,"line":41},[38,2432,2433],{},"💾 你的實體記憶體 (RAM)\n",[38,2435,2436],{"class":40,"line":47},[38,2437,2438],{},"├── 🟩 [靜態配置區] ── 啟動時直接「圈地封王」，位置固定，與程式同生共死 (Static)\n",[38,2440,2441],{"class":40,"line":62},[38,2442,2443],{},"├── 🟦 [Stack 棧區]  ── 隨著函式進出，CPU 指針上下跳動，用完秒回收 (Automatic)\n",[38,2445,2446],{"class":40,"line":173},[38,2447,2448],{},"└── 🟨 [Heap 堆區]   ── 程式執行時動態去申請一塊地，不用了派垃圾車回收 (Dynamic)\n",[11,2450,2451],{},"=========",[20,2453,2455],{"id":2454},"靜態配置static-allocation-就是你說的-static","靜態配置（Static Allocation）➔ 就是你說的 static",[11,2457,2458],{},"硬體行為： 當編譯器在打包你的程式碼時，看到 private static int currentID;，它就會在二進位檔案的標頭寫下：「這支程式開機時，請直接在 RAM 裡挖出一個固定的 4 位元組格子給它。」",[11,2460,2461],{},"物理現實： 程式一啟動，作業系統立刻把這個格子的物理地址（例如 0x00FF12）死死分配出去。在程式關閉前，這塊物理空間絕對不會釋放，沒有人能把它回收還給作業系統。因為它的地址和大小「永遠靜止不變」，所以這套機制在晶片層面就被稱為 「靜態配置」。",[11,2463,2451],{},[20,2465,2467],{"id":2466},"自動配置automatic-allocation-就是-stack","自動配置（Automatic Allocation）➔ 就是 Stack",[11,2469,2470],{},"硬體行為： 當你呼叫一個函式，CPU 的暫存器指針（Stack Pointer）就會往下移動，在 RAM 裡開闢一塊臨時空間。",[11,2472,2473],{},"物理現實： 當你的函式一執行完畢（遇到大括號結束 }），CPU 指針一彈回去，剛剛開闢的物理空間在硬體層面上就當場被標記為「可回收\u002F已釋放」。它隨著代碼執行「自動、瞬間」回收，速度極快。",[11,2475,2451],{},[20,2477,2479],{"id":2478},"動態配置dynamic-allocation-就是平民-heap","動態配置（Dynamic Allocation）➔ 就是平民 Heap",[11,2481,2482],{},"硬體行為： 你在程式執行到一半，突然寫了 new WorkItem()，程式才會臨時跑去跟作業系統大喊：「報告長官！現在使用者突然點了按鈕，請立刻在 Heap 區隨機撥一塊乾淨的土地給我蓋大樓！」",[11,2484,2485],{},"物理現實： 這塊土地的地址是動態決定的（每次 new 都不同）。當你的大樓沒人用了，C# 的垃圾回收機制（GC）就會啟動，把這塊動態申請來的物理空間擦乾淨，還給作業系統。",[11,2487,2451],{},[20,2489,2491],{"id":2490},"記憶體世界的生存與特權排行榜","記憶體世界的「生存與特權」排行榜",[11,2493,2451],{},[20,2495,2497],{"id":2496},"程式碼世界的存取權限access-modifiers大小","程式碼世界的「存取權限（Access Modifiers）」大小",[272,2499],{},[56,2501,58],{},{"title":33,"searchDepth":47,"depth":47,"links":2503},[2504,2510,2514,2521,2529,2535],{"id":996,"depth":47,"text":997,"children":2505},[2506,2507,2508,2509],{"id":1000,"depth":62,"text":1001},{"id":1033,"depth":62,"text":1034},{"id":1143,"depth":62,"text":1144},{"id":1372,"depth":62,"text":1372},{"id":1424,"depth":47,"text":1425,"children":2511},[2512,2513],{"id":1428,"depth":62,"text":1429},{"id":1443,"depth":62,"text":1444},{"id":1468,"depth":47,"text":1469,"children":2515},[2516,2517,2518,2519,2520],{"id":1472,"depth":62,"text":1473},{"id":1484,"depth":62,"text":1485},{"id":1499,"depth":62,"text":1500},{"id":1509,"depth":62,"text":1509},{"id":1525,"depth":62,"text":1526},{"id":1642,"depth":47,"text":1643,"children":2522},[2523,2524,2525,2526,2527,2528],{"id":1646,"depth":62,"text":1646},{"id":1761,"depth":62,"text":1761},{"id":1813,"depth":62,"text":1813},{"id":1961,"depth":62,"text":1961},{"id":2015,"depth":62,"text":2015},{"id":2051,"depth":62,"text":2051},{"id":2056,"depth":47,"text":2057,"children":2530},[2531,2532,2533,2534],{"id":2065,"depth":62,"text":2065},{"id":2100,"depth":62,"text":2100},{"id":2258,"depth":62,"text":2259},{"id":830,"depth":62,"text":2405},{"id":2410,"depth":47,"text":2411,"children":2536},[2537,2538,2539,2540,2541,2542],{"id":2419,"depth":62,"text":2420},{"id":2454,"depth":62,"text":2455},{"id":2466,"depth":62,"text":2467},{"id":2478,"depth":62,"text":2479},{"id":2490,"depth":62,"text":2491},{"id":2496,"depth":62,"text":2497},{},"\u002Fnotes\u002Fsync\u002Fcsharp-01",{"title":991,"description":1004},"C#\u002F易混淆概念\u002F易混淆概念01.md","notes\u002Fsync\u002Fcsharp-易混淆概念-易混淆概念01",[76,886],"dtNraGKifhA7u6b_gTCZcOwUWN3cjavKcf9g51k3FoE",{"id":2551,"title":2552,"body":2553,"category":2640,"date":2641,"description":2642,"draft":67,"extension":68,"meta":2643,"navigation":70,"path":2644,"seo":2645,"sourcePath":2646,"stem":2647,"tags":2648,"__hash__":2651},"notes\u002Fnotes\u002Fvue-reactivity-basics.md","Vue 反應性基礎",{"type":8,"value":2554,"toc":2633},[2555,2558,2561,2571,2587,2596,2599,2619,2622],[85,2556,2557],{"id":2557},"為什麼要懂反應性",[11,2559,2560],{},"Vue 的畫面更新建立在「讀取被追蹤、寫入觸發更新」。搞清楚這條路徑，比背 API 名稱更重要。",[85,2562,2564,2567,2568],{"id":2563},"ref-與-reactive",[35,2565,2566],{},"ref"," 與 ",[35,2569,2570],{},"reactive",[122,2572,2573,2582],{},[125,2574,2575,2577,2578,2581],{},[35,2576,2566],{},"：包一層 ",[35,2579,2580],{},".value","，適合基本型別與需要整包替換的值。",[125,2583,2584,2586],{},[35,2585,2570],{},"：對物件做 Proxy，適合結構穩定的狀態物件。",[11,2588,2589,2590,2592,2593,2595],{},"實務上元件內狀態多數用 ",[35,2591,2566],{}," 就夠；跨多欄位的表單模型再用 ",[35,2594,2570],{}," 也行。",[85,2597,2598],{"id":2598},"常見陷阱",[387,2600,2601,2610,2613],{},[125,2602,2603,2604,2606,2607,142],{},"解構 ",[35,2605,2570],{}," 物件會失去追蹤，需要 ",[35,2608,2609],{},"toRefs",[125,2611,2612],{},"在非同步 callback 裡讀寫仍要保持同一份來源。",[125,2614,2615,2616,2618],{},"大物件全程 ",[35,2617,2570],{}," 可能讓依賴圖難除錯，寧可拆小。",[85,2620,2621],{"id":2621},"小結",[11,2623,2624,2625,642,2627,2629,2630,2632],{},"先確認「誰在讀、誰在寫」，再選 ",[35,2626,2566],{},[35,2628,2570],{},"。大部分 UI 狀態用 ",[35,2631,2566],{}," 起步最穩。",{"title":33,"searchDepth":47,"depth":47,"links":2634},[2635,2636,2638,2639],{"id":2557,"depth":47,"text":2557},{"id":2563,"depth":47,"text":2637},"ref 與 reactive",{"id":2598,"depth":47,"text":2598},{"id":2621,"depth":47,"text":2621},"frontend","2026-08-20","整理 ref、reactive 與依賴追蹤的核心觀念，方便之後回頭對照實務行為。",{},"\u002Fnotes\u002Fvue-reactivity-basics",{"title":2552,"description":2642},null,"notes\u002Fvue-reactivity-basics",[2649,2650],"Vue","Reactivity","e5PoVDMtWlCgwyoX1OBUG05C5O7jGyJYjrMdSQexMIc",{"id":2653,"title":2654,"body":2655,"category":64,"date":2659,"description":2654,"draft":67,"extension":68,"meta":2660,"navigation":70,"path":2661,"seo":2662,"sourcePath":2663,"stem":2664,"tags":2665,"__hash__":2666},"notes\u002Fnotes\u002Fsync\u002Fcsharp-測試.md","測試",{"type":8,"value":2656,"toc":2657},[],{"title":33,"searchDepth":47,"depth":47,"links":2658},[],"2026-08-18",{},"\u002Fnotes\u002Fsync\u002Fcsharp",{"title":2654,"description":2654},"C#\u002F測試.md","notes\u002Fsync\u002Fcsharp-測試",[76],"fbz6_-PGAIRWpAnpfsphK7AESFTEur2BK7JAv-xiBes",{"id":2668,"title":2669,"body":2670,"category":64,"date":2985,"description":2986,"draft":67,"extension":68,"meta":2987,"navigation":70,"path":2988,"seo":2989,"sourcePath":2990,"stem":2991,"tags":2992,"__hash__":2994},"notes\u002Fnotes\u002Fsync\u002Fcsharp-開發-resoucehub-program-cs.md","Program.cs",{"type":8,"value":2671,"toc":2977},[2672,2780,2790,2794,2802,2848,2853,2864,2870,2874,2890,2894,2898,2922,2928,2933,2947,2975],[28,2673,2675],{"className":30,"code":2674,"language":32,"meta":33,"style":33},"using Microsoft.EntityFrameworkCore;\nusing ResourceHub.Data;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllers();\nbuilder.Services.AddDbContext\u003CAppDbContext>(options =>\n    options.UseInMemoryDatabase(\"ResourceListDb\"));\nbuilder.Services.AddOpenApi();\n\nvar app = builder.Build();\n\n\u002F\u002F Configure the HTTP request pipeline.\nif (app.Environment.IsDevelopment())\n{\n    app.MapOpenApi();\n}\n\napp.UseHttpsRedirection();\napp.UseAuthorization();\napp.MapControllers();\napp.Run();\n",[35,2676,2677,2682,2687,2691,2696,2700,2705,2710,2715,2720,2724,2729,2733,2738,2743,2747,2752,2756,2760,2765,2770,2775],{"__ignoreMap":33},[38,2678,2679],{"class":40,"line":41},[38,2680,2681],{},"using Microsoft.EntityFrameworkCore;\n",[38,2683,2684],{"class":40,"line":47},[38,2685,2686],{},"using ResourceHub.Data;\n",[38,2688,2689],{"class":40,"line":62},[38,2690,194],{"emptyLinePlaceholder":70},[38,2692,2693],{"class":40,"line":173},[38,2694,2695],{},"var builder = WebApplication.CreateBuilder(args);\n",[38,2697,2698],{"class":40,"line":179},[38,2699,194],{"emptyLinePlaceholder":70},[38,2701,2702],{"class":40,"line":185},[38,2703,2704],{},"builder.Services.AddControllers();\n",[38,2706,2707],{"class":40,"line":191},[38,2708,2709],{},"builder.Services.AddDbContext\u003CAppDbContext>(options =>\n",[38,2711,2712],{"class":40,"line":197},[38,2713,2714],{},"    options.UseInMemoryDatabase(\"ResourceListDb\"));\n",[38,2716,2717],{"class":40,"line":203},[38,2718,2719],{},"builder.Services.AddOpenApi();\n",[38,2721,2722],{"class":40,"line":209},[38,2723,194],{"emptyLinePlaceholder":70},[38,2725,2726],{"class":40,"line":215},[38,2727,2728],{},"var app = builder.Build();\n",[38,2730,2731],{"class":40,"line":221},[38,2732,194],{"emptyLinePlaceholder":70},[38,2734,2735],{"class":40,"line":227},[38,2736,2737],{},"\u002F\u002F Configure the HTTP request pipeline.\n",[38,2739,2740],{"class":40,"line":232},[38,2741,2742],{},"if (app.Environment.IsDevelopment())\n",[38,2744,2745],{"class":40,"line":238},[38,2746,165],{},[38,2748,2749],{"class":40,"line":244},[38,2750,2751],{},"    app.MapOpenApi();\n",[38,2753,2754],{"class":40,"line":249},[38,2755,270],{},[38,2757,2758],{"class":40,"line":255},[38,2759,194],{"emptyLinePlaceholder":70},[38,2761,2762],{"class":40,"line":261},[38,2763,2764],{},"app.UseHttpsRedirection();\n",[38,2766,2767],{"class":40,"line":267},[38,2768,2769],{},"app.UseAuthorization();\n",[38,2771,2772],{"class":40,"line":1264},[38,2773,2774],{},"app.MapControllers();\n",[38,2776,2777],{"class":40,"line":1269},[38,2778,2779],{},"app.Run();\n",[20,2781,2783,2786,2787],{"id":2782},"microsoftentityframeworkcore與resourcehubdata",[35,2784,2785],{},"Microsoft.EntityFrameworkCore","與",[35,2788,2789],{},"ResourceHub.Data",[20,2791,2793],{"id":2792},"webapplicationcreatebuilderargs","`WebApplication.CreateBuilder(args);",[89,2795,2796],{},[11,2797,2798,2801],{},[35,2799,2800],{},"WebApplication.CreateBuilder(args)"," 就是整個 .NET 專案的「萬物本源」",[122,2803,2804,2818,2824,2834],{},[125,2805,2806,2809,2810,2813,2814,2817],{},[15,2807,2808],{},"讀取環境變數與設定檔："," 它會自動去抓取 ",[35,2811,2812],{},"appsettings.json","（類似 Node.js 的 ",[35,2815,2816],{},".env","）裡面的設定。",[125,2819,2820,2823],{},[15,2821,2822],{},"設定基礎 Log 系統："," 讓你在終端機可以看到那些黃黃綠綠的系統提示文字。",[125,2825,2826,2829,2830,2833],{},[15,2827,2828],{},"建立依賴注入 (DI) 的大倉庫："," 也就是為後面的 ",[35,2831,2832],{},"builder.Services"," 準備好空間。",[125,2835,2836,2839,2840,2843,2844,2847],{},[15,2837,2838],{},"接收外部參數："," 括號裡的 ",[35,2841,2842],{},"args"," 就是終端機下指令時傳進來的參數（例如 ",[35,2845,2846],{},"dotnet run --environment Production","），它會把這些參數吃進來。",[20,2849,2851],{"id":2850},"builderservices",[35,2852,2832],{},[11,2854,2855,2856,2859,2860,2863],{},"在 ",[35,2857,2858],{},"builder.Build()"," 執行之前，所有開頭為 ",[35,2861,2862],{},"builder.Services.Add..."," 的程式碼，都屬於「註冊服務 (Service Registration)」階段。",[20,2865,2867],{"id":2866},"builderservicesaddcontrollers",[35,2868,2869],{},"builder.Services.AddControllers();",[608,2871,2873],{"id":2872},"_1-自動掃描並註冊所有的-controller","1. 自動掃描並註冊所有的 Controller",[122,2875,2876,2879],{},[125,2877,2878],{},"當程式啟動執行到這一行時，系統會自動像雷達一樣掃描你整個專案",[125,2880,2881,2882,2885,2886,2889],{},"只要發現有任何類別繼承了 ",[35,2883,2884],{},"ControllerBase","（例如你剛剛寫的 ",[35,2887,2888],{},"ResourcesController","），就會把它註冊到系統的「依賴注入 (DI) 容器」裡，正式成為處理 HTTP 請求的合法窗口。你不需要像其他框架那樣，每寫一個 Controller 就手動去某個陣列裡加一筆。",[608,2891,2893],{"id":2892},"_2-啟用自動資料轉換-model-binding","2. 啟用自動資料轉換 (Model Binding)",[608,2895,2897],{"id":2896},"_3專注於-api-不渲染-html-畫面","3.專注於 API (不渲染 HTML 畫面)",[122,2899,2900,2906,2912],{},[125,2901,2902,2905],{},[35,2903,2904],{},"AddControllersWithViews()","：這是給傳統 MVC 用的（後端要負責把資料塞進 HTML 模板裡吐給前端）。",[125,2907,2908,2911],{},[35,2909,2910],{},"AddRazorPages()","：這是給另一種後端渲染網頁用的。",[125,2913,2914,2917,2918,2921],{},[35,2915,2916],{},"AddControllers()","：這是",[15,2919,2920],{},"最輕量","的註冊方式。它明確告訴系統：「我這個專案是一個純粹的 Web API 後端，我只回傳 JSON 格式的資料，不處理任何 HTML 畫面。」",[20,2923,2925],{"id":2924},"var-app-builderbuild",[35,2926,2927],{},"var app = builder.Build();",[89,2929,2930],{},[11,2931,2932],{},"這個才是真正的「成型」時刻。",[11,2934,2935,2936,2939,2940,2943,2944,336],{},"當你把所有的 Controller、資料庫 (",[35,2937,2938],{},"AddDbContext",") 都丟進 ",[35,2941,2942],{},"builder"," 之後，一旦呼叫了 ",[35,2945,2946],{},".Build()",[122,2948,2949,2952,2959],{},[125,2950,2951],{},"這個大倉庫就會被「鎖死」。",[125,2953,2954,2955,2958],{},"系統會把剛才註冊的所有東西打包，真正實例化出一個可以處理 HTTP 請求的 Web 伺服器物件 (",[35,2956,2957],{},"app",")。",[125,2960,2961,2964,2965,2967,2968,2971,2972,2974],{},[15,2962,2963],{},"注意："," 在 ",[35,2966,2946],{}," 執行之後，你就",[15,2969,2970],{},"再也不能","往 ",[35,2973,2832],{}," 裡面加新東西了。",[56,2976,58],{},{"title":33,"searchDepth":47,"depth":47,"links":2978},[2979,2981,2982,2983,2984],{"id":2782,"depth":62,"text":2980},"Microsoft.EntityFrameworkCore與ResourceHub.Data",{"id":2792,"depth":62,"text":2793},{"id":2850,"depth":62,"text":2832},{"id":2866,"depth":62,"text":2869},{"id":2924,"depth":62,"text":2927},"2026-08-10","using Microsoft.EntityFrameworkCore;",{},"\u002Fnotes\u002Fsync\u002Fcsharp-resoucehub-program-cs",{"title":2669,"description":2986},"C#\u002F開發\u002FResouceHub\u002FProgram.cs.md","notes\u002Fsync\u002Fcsharp-開發-resoucehub-program-cs",[76,2993],"Practice","xsAhmLFcWbHOoykeWv7CaXPzrdCs0M2FHq8bWyjZpH0",{"id":2996,"title":2997,"body":2998,"category":64,"date":4138,"description":4139,"draft":67,"extension":68,"meta":4140,"navigation":70,"path":4141,"seo":4142,"sourcePath":4143,"stem":4144,"tags":4145,"__hash__":4146},"notes\u002Fnotes\u002Fsync\u002Fcsharp-易混淆概念-易混淆概念02.md","易混淆概念02",{"type":8,"value":2999,"toc":4125},[3000,3003,3477,3480,3501,3504,3528,3530,3550,3552,3562,3600,3603,3611,3613,3616,3625,3641,3643,3648,3731,3733,3738,3746,3801,3803,3808,3835,3838,3842,3850,3852,3856,3859,3862,3899,3903,3909,3953,3956,3960,3980,3982,3986,3989,4044,4046,4050,4053,4057,4068,4076,4079,4083,4086,4095,4103,4107,4121,4123],[85,3001,3002],{"id":3002},"物件的繼承",[28,3004,3006],{"className":1794,"code":3005,"language":1796,"meta":33,"style":33},"\u002F\u002F WorkItem implicitly inherits from the Object class.\npublic class WorkItem\n{\n    \u002F\u002F Static field currentID stores the job ID of the last WorkItem that\n    \u002F\u002F has been created.\n    private static int currentID;\n\n    \u002F\u002FProperties.\n    protected int ID { get; set; }\n    protected string Title { get; set; }\n    protected string Description { get; set; }\n    protected TimeSpan jobLength { get; set; }\n\n    \u002F\u002F Default constructor. If a derived class does not invoke a base-\n    \u002F\u002F class constructor explicitly, the default constructor is called\n    \u002F\u002F implicitly.\n    public WorkItem()\n    {\n        ID = 0;\n        Title = \"Default title\";\n        Description = \"Default description.\";\n        jobLength = new TimeSpan();\n    }\n\n    \u002F\u002F Instance constructor that has three parameters.\n    public WorkItem(string title, string desc, TimeSpan joblen)\n    {\n        ID = GetNextID();\n        Title = title;\n        Description = desc;\n        jobLength = joblen;\n    }\n\n    \u002F\u002F Static constructor to initialize the static member, currentID. This\n    \u002F\u002F constructor is called one time, automatically, before any instance\n    \u002F\u002F of WorkItem or ChangeRequest is created, or currentID is referenced.\n    static WorkItem() => currentID = 0;\n\n    \u002F\u002F currentID is a static field. It is incremented each time a new\n    \u002F\u002F instance of WorkItem is created.\n    protected int GetNextID() => ++currentID;\n\n    \u002F\u002F Method Update enables you to update the title and job length of an\n    \u002F\u002F existing WorkItem object.\n    public void Update(string title, TimeSpan joblen)\n    {\n        this.Title = title;\n        this.jobLength = joblen;\n    }\n\n    \u002F\u002F Virtual method override of the ToString method that is inherited\n    \u002F\u002F from System.Object.\n    public override string ToString() =>\n        $\"{this.ID} - {this.Title}\";\n}\n\n\u002F\u002F ChangeRequest derives from WorkItem and adds a property (originalItemID)\n\u002F\u002F and two constructors.\npublic class ChangeRequest : WorkItem\n{\n    protected int originalItemID { get; set; }\n\n    \u002F\u002F Constructors. Because neither constructor calls a base-class\n    \u002F\u002F constructor explicitly, the default constructor in the base class\n    \u002F\u002F is called implicitly. The base class must contain a default\n    \u002F\u002F constructor.\n\n    \u002F\u002F Default constructor for the derived class.\n    public ChangeRequest() { }\n\n    \u002F\u002F Instance constructor that has four parameters.\n    public ChangeRequest(string title, string desc, TimeSpan jobLen,\n                         int originalID)\n    {\n        \u002F\u002F The following properties and the GetNexID method are inherited\n        \u002F\u002F from WorkItem.\n        this.ID = GetNextID();\n        this.Title = title;\n        this.Description = desc;\n        this.jobLength = jobLen;\n\n        \u002F\u002F Property originalItemID is a member of ChangeRequest, but not\n        \u002F\u002F of WorkItem.\n        this.originalItemID = originalID;\n    }\n}\n",[35,3007,3008,3013,3018,3022,3027,3032,3037,3041,3046,3051,3056,3061,3066,3070,3075,3080,3085,3090,3094,3099,3104,3109,3114,3119,3124,3130,3136,3141,3147,3153,3159,3165,3170,3175,3181,3187,3193,3199,3204,3210,3216,3222,3227,3233,3239,3245,3250,3256,3262,3267,3272,3278,3284,3290,3296,3301,3306,3312,3318,3324,3329,3335,3340,3346,3352,3358,3364,3369,3375,3381,3386,3392,3398,3404,3409,3415,3421,3427,3432,3438,3444,3449,3455,3461,3467,3472],{"__ignoreMap":33},[38,3009,3010],{"class":40,"line":41},[38,3011,3012],{},"\u002F\u002F WorkItem implicitly inherits from the Object class.\n",[38,3014,3015],{"class":40,"line":47},[38,3016,3017],{},"public class WorkItem\n",[38,3019,3020],{"class":40,"line":62},[38,3021,165],{},[38,3023,3024],{"class":40,"line":173},[38,3025,3026],{},"    \u002F\u002F Static field currentID stores the job ID of the last WorkItem that\n",[38,3028,3029],{"class":40,"line":179},[38,3030,3031],{},"    \u002F\u002F has been created.\n",[38,3033,3034],{"class":40,"line":185},[38,3035,3036],{},"    private static int currentID;\n",[38,3038,3039],{"class":40,"line":191},[38,3040,194],{"emptyLinePlaceholder":70},[38,3042,3043],{"class":40,"line":197},[38,3044,3045],{},"    \u002F\u002FProperties.\n",[38,3047,3048],{"class":40,"line":203},[38,3049,3050],{},"    protected int ID { get; set; }\n",[38,3052,3053],{"class":40,"line":209},[38,3054,3055],{},"    protected string Title { get; set; }\n",[38,3057,3058],{"class":40,"line":215},[38,3059,3060],{},"    protected string Description { get; set; }\n",[38,3062,3063],{"class":40,"line":221},[38,3064,3065],{},"    protected TimeSpan jobLength { get; set; }\n",[38,3067,3068],{"class":40,"line":227},[38,3069,194],{"emptyLinePlaceholder":70},[38,3071,3072],{"class":40,"line":232},[38,3073,3074],{},"    \u002F\u002F Default constructor. If a derived class does not invoke a base-\n",[38,3076,3077],{"class":40,"line":238},[38,3078,3079],{},"    \u002F\u002F class constructor explicitly, the default constructor is called\n",[38,3081,3082],{"class":40,"line":244},[38,3083,3084],{},"    \u002F\u002F implicitly.\n",[38,3086,3087],{"class":40,"line":249},[38,3088,3089],{},"    public WorkItem()\n",[38,3091,3092],{"class":40,"line":255},[38,3093,176],{},[38,3095,3096],{"class":40,"line":261},[38,3097,3098],{},"        ID = 0;\n",[38,3100,3101],{"class":40,"line":267},[38,3102,3103],{},"        Title = \"Default title\";\n",[38,3105,3106],{"class":40,"line":1264},[38,3107,3108],{},"        Description = \"Default description.\";\n",[38,3110,3111],{"class":40,"line":1269},[38,3112,3113],{},"        jobLength = new TimeSpan();\n",[38,3115,3117],{"class":40,"line":3116},23,[38,3118,264],{},[38,3120,3122],{"class":40,"line":3121},24,[38,3123,194],{"emptyLinePlaceholder":70},[38,3125,3127],{"class":40,"line":3126},25,[38,3128,3129],{},"    \u002F\u002F Instance constructor that has three parameters.\n",[38,3131,3133],{"class":40,"line":3132},26,[38,3134,3135],{},"    public WorkItem(string title, string desc, TimeSpan joblen)\n",[38,3137,3139],{"class":40,"line":3138},27,[38,3140,176],{},[38,3142,3144],{"class":40,"line":3143},28,[38,3145,3146],{},"        ID = GetNextID();\n",[38,3148,3150],{"class":40,"line":3149},29,[38,3151,3152],{},"        Title = title;\n",[38,3154,3156],{"class":40,"line":3155},30,[38,3157,3158],{},"        Description = desc;\n",[38,3160,3162],{"class":40,"line":3161},31,[38,3163,3164],{},"        jobLength = joblen;\n",[38,3166,3168],{"class":40,"line":3167},32,[38,3169,264],{},[38,3171,3173],{"class":40,"line":3172},33,[38,3174,194],{"emptyLinePlaceholder":70},[38,3176,3178],{"class":40,"line":3177},34,[38,3179,3180],{},"    \u002F\u002F Static constructor to initialize the static member, currentID. This\n",[38,3182,3184],{"class":40,"line":3183},35,[38,3185,3186],{},"    \u002F\u002F constructor is called one time, automatically, before any instance\n",[38,3188,3190],{"class":40,"line":3189},36,[38,3191,3192],{},"    \u002F\u002F of WorkItem or ChangeRequest is created, or currentID is referenced.\n",[38,3194,3196],{"class":40,"line":3195},37,[38,3197,3198],{},"    static WorkItem() => currentID = 0;\n",[38,3200,3202],{"class":40,"line":3201},38,[38,3203,194],{"emptyLinePlaceholder":70},[38,3205,3207],{"class":40,"line":3206},39,[38,3208,3209],{},"    \u002F\u002F currentID is a static field. It is incremented each time a new\n",[38,3211,3213],{"class":40,"line":3212},40,[38,3214,3215],{},"    \u002F\u002F instance of WorkItem is created.\n",[38,3217,3219],{"class":40,"line":3218},41,[38,3220,3221],{},"    protected int GetNextID() => ++currentID;\n",[38,3223,3225],{"class":40,"line":3224},42,[38,3226,194],{"emptyLinePlaceholder":70},[38,3228,3230],{"class":40,"line":3229},43,[38,3231,3232],{},"    \u002F\u002F Method Update enables you to update the title and job length of an\n",[38,3234,3236],{"class":40,"line":3235},44,[38,3237,3238],{},"    \u002F\u002F existing WorkItem object.\n",[38,3240,3242],{"class":40,"line":3241},45,[38,3243,3244],{},"    public void Update(string title, TimeSpan joblen)\n",[38,3246,3248],{"class":40,"line":3247},46,[38,3249,176],{},[38,3251,3253],{"class":40,"line":3252},47,[38,3254,3255],{},"        this.Title = title;\n",[38,3257,3259],{"class":40,"line":3258},48,[38,3260,3261],{},"        this.jobLength = joblen;\n",[38,3263,3265],{"class":40,"line":3264},49,[38,3266,264],{},[38,3268,3270],{"class":40,"line":3269},50,[38,3271,194],{"emptyLinePlaceholder":70},[38,3273,3275],{"class":40,"line":3274},51,[38,3276,3277],{},"    \u002F\u002F Virtual method override of the ToString method that is inherited\n",[38,3279,3281],{"class":40,"line":3280},52,[38,3282,3283],{},"    \u002F\u002F from System.Object.\n",[38,3285,3287],{"class":40,"line":3286},53,[38,3288,3289],{},"    public override string ToString() =>\n",[38,3291,3293],{"class":40,"line":3292},54,[38,3294,3295],{},"        $\"{this.ID} - {this.Title}\";\n",[38,3297,3299],{"class":40,"line":3298},55,[38,3300,270],{},[38,3302,3304],{"class":40,"line":3303},56,[38,3305,194],{"emptyLinePlaceholder":70},[38,3307,3309],{"class":40,"line":3308},57,[38,3310,3311],{},"\u002F\u002F ChangeRequest derives from WorkItem and adds a property (originalItemID)\n",[38,3313,3315],{"class":40,"line":3314},58,[38,3316,3317],{},"\u002F\u002F and two constructors.\n",[38,3319,3321],{"class":40,"line":3320},59,[38,3322,3323],{},"public class ChangeRequest : WorkItem\n",[38,3325,3327],{"class":40,"line":3326},60,[38,3328,165],{},[38,3330,3332],{"class":40,"line":3331},61,[38,3333,3334],{},"    protected int originalItemID { get; set; }\n",[38,3336,3338],{"class":40,"line":3337},62,[38,3339,194],{"emptyLinePlaceholder":70},[38,3341,3343],{"class":40,"line":3342},63,[38,3344,3345],{},"    \u002F\u002F Constructors. Because neither constructor calls a base-class\n",[38,3347,3349],{"class":40,"line":3348},64,[38,3350,3351],{},"    \u002F\u002F constructor explicitly, the default constructor in the base class\n",[38,3353,3355],{"class":40,"line":3354},65,[38,3356,3357],{},"    \u002F\u002F is called implicitly. The base class must contain a default\n",[38,3359,3361],{"class":40,"line":3360},66,[38,3362,3363],{},"    \u002F\u002F constructor.\n",[38,3365,3367],{"class":40,"line":3366},67,[38,3368,194],{"emptyLinePlaceholder":70},[38,3370,3372],{"class":40,"line":3371},68,[38,3373,3374],{},"    \u002F\u002F Default constructor for the derived class.\n",[38,3376,3378],{"class":40,"line":3377},69,[38,3379,3380],{},"    public ChangeRequest() { }\n",[38,3382,3384],{"class":40,"line":3383},70,[38,3385,194],{"emptyLinePlaceholder":70},[38,3387,3389],{"class":40,"line":3388},71,[38,3390,3391],{},"    \u002F\u002F Instance constructor that has four parameters.\n",[38,3393,3395],{"class":40,"line":3394},72,[38,3396,3397],{},"    public ChangeRequest(string title, string desc, TimeSpan jobLen,\n",[38,3399,3401],{"class":40,"line":3400},73,[38,3402,3403],{},"                         int originalID)\n",[38,3405,3407],{"class":40,"line":3406},74,[38,3408,176],{},[38,3410,3412],{"class":40,"line":3411},75,[38,3413,3414],{},"        \u002F\u002F The following properties and the GetNexID method are inherited\n",[38,3416,3418],{"class":40,"line":3417},76,[38,3419,3420],{},"        \u002F\u002F from WorkItem.\n",[38,3422,3424],{"class":40,"line":3423},77,[38,3425,3426],{},"        this.ID = GetNextID();\n",[38,3428,3430],{"class":40,"line":3429},78,[38,3431,3255],{},[38,3433,3435],{"class":40,"line":3434},79,[38,3436,3437],{},"        this.Description = desc;\n",[38,3439,3441],{"class":40,"line":3440},80,[38,3442,3443],{},"        this.jobLength = jobLen;\n",[38,3445,3447],{"class":40,"line":3446},81,[38,3448,194],{"emptyLinePlaceholder":70},[38,3450,3452],{"class":40,"line":3451},82,[38,3453,3454],{},"        \u002F\u002F Property originalItemID is a member of ChangeRequest, but not\n",[38,3456,3458],{"class":40,"line":3457},83,[38,3459,3460],{},"        \u002F\u002F of WorkItem.\n",[38,3462,3464],{"class":40,"line":3463},84,[38,3465,3466],{},"        this.originalItemID = originalID;\n",[38,3468,3470],{"class":40,"line":3469},85,[38,3471,264],{},[38,3473,3475],{"class":40,"line":3474},86,[38,3476,270],{},[20,3478,3479],{"id":3479},"絕對事實",[387,3481,3482],{},[125,3483,3484,3485,3488,3489,3492,3493],{},"一個物件的",[35,3486,3487],{},"建構子","跟",[35,3490,3491],{},"物件本身","需要同名\n",[122,3494,3495,3498],{},[125,3496,3497],{},"兒子的類別叫 ChangeRequest，他的建構子就只能叫 public ChangeRequest()。",[125,3499,3500],{},"老爸的 public WorkItem() 名字天生不合，所以絕對無法被繼承。",[11,3502,3503],{},"+++++++++++++",[387,3505,3506],{"start":47},[125,3507,3508,3509,3512,3513,3516,3517,3519,3520],{},"繼承的部分是",[35,3510,3511],{},"屬性","+",[35,3514,3515],{},"方法","，",[35,3518,3487],{},"無法被繼承：\n",[122,3521,3522],{},[125,3523,3524,3525],{},"因為",[35,3526,3527],{},"建構子跟物件本身需要同名",[11,3529,3503],{},[387,3531,3532],{"start":62},[125,3533,3534,3535,3488,3537,3539,3540,3545],{},"繼承來的",[35,3536,3511],{},[35,3538,3515],{},"可以隨意使用、也可以不用\n",[3541,3542],"img",{"alt":3543,"src":3544},"image","https:\u002F\u002Fhackmd.io\u002F_uploads\u002FSyFxL_Olzx.png",[122,3546,3547],{},[125,3548,3549],{},"兒子(ChangeRequest)擁有這些資產的絕對主導權",[11,3551,3503],{},[387,3553,3554],{"start":173},[125,3555,3556,3557],{},"孫子繼承兒子，即使兒子沒說明，孫子也能拿到曾祖父的財產？\n",[122,3558,3559],{},[125,3560,3561],{},"在物件導向裡叫做「傳遞性（Transitivity）」",[28,3563,3565],{"className":1649,"code":3564,"language":1651,"meta":33,"style":33},"👴 曾祖父 Object  ➔ 留下了 ToString() 技能\n  │\n👨 爺爺 WorkItem  ➔ 繼承了 ToString()，自己又發明了 ID, Title, Update()\n  │\n👦 爸爸 ChangeRequest ➔ 什麼都不寫（肚子裡空空如也）\n  │\n👶 孫子 SuperChangeRequest ➔ 宣布繼承爸爸！\n",[35,3566,3567,3572,3577,3582,3586,3591,3595],{"__ignoreMap":33},[38,3568,3569],{"class":40,"line":41},[38,3570,3571],{},"👴 曾祖父 Object  ➔ 留下了 ToString() 技能\n",[38,3573,3574],{"class":40,"line":47},[38,3575,3576],{},"  │\n",[38,3578,3579],{"class":40,"line":62},[38,3580,3581],{},"👨 爺爺 WorkItem  ➔ 繼承了 ToString()，自己又發明了 ID, Title, Update()\n",[38,3583,3584],{"class":40,"line":173},[38,3585,3576],{},[38,3587,3588],{"class":40,"line":179},[38,3589,3590],{},"👦 爸爸 ChangeRequest ➔ 什麼都不寫（肚子裡空空如也）\n",[38,3592,3593],{"class":40,"line":185},[38,3594,3576],{},[38,3596,3597],{"class":40,"line":191},[38,3598,3599],{},"👶 孫子 SuperChangeRequest ➔ 宣布繼承爸爸！\n",[11,3601,3602],{},"這時候，這個孫子（SuperChangeRequest）落地時，他的記憶體大樓會發生神蹟：",[387,3604,3605,3608],{},[125,3606,3607],{},"他往上看，爸爸雖然是一隻空白的 Class，但爸爸的血脈裡流著爺爺的基因。",[125,3609,3610],{},"所以，孫子全自動、不需要任何人敘明，直接打包繼承了爺爺的 ID、Title、Update()，甚至曾祖父的 ToString()！",[11,3612,3503],{},[11,3614,3615],{},"5.那為什麼建構子的大括號 { } 內部，會出現屬性的名字（ID、Title）？這不就代表建構子內部『有』屬性嗎？",[28,3617,3619],{"className":1794,"code":3618,"language":1796,"meta":33,"style":33},"Title = title;\n",[35,3620,3621],{"__ignoreMap":33},[38,3622,3623],{"class":40,"line":41},[38,3624,3618],{},[122,3626,3627,3638],{},[125,3628,3629,3630],{},"在記憶體的實體世界裡，這行代碼要拆成左邊與右邊：\n",[122,3631,3632,3635],{},[125,3633,3634],{},"右邊的 title（參數）：它是外面貨車送來的全新材料（例如字串 \"Default title\"）。它確實暫時活在建構子的肚子（Stack 區域）裡。",[125,3636,3637],{},"左邊的 Title（屬性）：它是死死固定在 Heap 區大樓外牆上的「實體信箱」。",[125,3639,3640],{},"建構子這個「郵差」，在執行期間走到大樓外牆前，伸手把貨車上的材料（右邊的 title），塞進大樓原本就有的橘色信箱（左邊的 Title）裡。",[11,3642,3503],{},[387,3644,3645],{"start":185},[125,3646,3647],{},"「定義」與「呼叫」的物理界線",[28,3649,3651],{"className":1794,"code":3650,"language":1796,"meta":33,"style":33},"public class WorkItem\n{\n    \u002F\u002F ===================================================\n    \u002F\u002F 🧱 【這是定義】真正挖在 Heap 區大樓肉身上的「實體記憶體格子」\n    \u002F\u002F ===================================================\n    protected int ID { get; set; }\n    protected string Title { get; set; }\n\n\n    \u002F\u002F ===================================================\n    \u002F\u002F 🛠️ 【這是動作】一段通電執行的機器碼，肚子裡只有「通電指令」\n    \u002F\u002F ===================================================\n    public WorkItem(string title)\n    {\n        \u002F\u002F 🚨 物理真相：這不叫「內含屬性」，這叫「指令：把 title 的電流導向牆上的 Title 格子」\n        this.Title = title; \n    }\n}\n",[35,3652,3653,3657,3661,3666,3671,3675,3679,3683,3687,3691,3695,3700,3704,3709,3713,3718,3723,3727],{"__ignoreMap":33},[38,3654,3655],{"class":40,"line":41},[38,3656,3017],{},[38,3658,3659],{"class":40,"line":47},[38,3660,165],{},[38,3662,3663],{"class":40,"line":62},[38,3664,3665],{},"    \u002F\u002F ===================================================\n",[38,3667,3668],{"class":40,"line":173},[38,3669,3670],{},"    \u002F\u002F 🧱 【這是定義】真正挖在 Heap 區大樓肉身上的「實體記憶體格子」\n",[38,3672,3673],{"class":40,"line":179},[38,3674,3665],{},[38,3676,3677],{"class":40,"line":185},[38,3678,3050],{},[38,3680,3681],{"class":40,"line":191},[38,3682,3055],{},[38,3684,3685],{"class":40,"line":197},[38,3686,194],{"emptyLinePlaceholder":70},[38,3688,3689],{"class":40,"line":203},[38,3690,194],{"emptyLinePlaceholder":70},[38,3692,3693],{"class":40,"line":209},[38,3694,3665],{},[38,3696,3697],{"class":40,"line":215},[38,3698,3699],{},"    \u002F\u002F 🛠️ 【這是動作】一段通電執行的機器碼，肚子裡只有「通電指令」\n",[38,3701,3702],{"class":40,"line":221},[38,3703,3665],{},[38,3705,3706],{"class":40,"line":227},[38,3707,3708],{},"    public WorkItem(string title)\n",[38,3710,3711],{"class":40,"line":232},[38,3712,176],{},[38,3714,3715],{"class":40,"line":238},[38,3716,3717],{},"        \u002F\u002F 🚨 物理真相：這不叫「內含屬性」，這叫「指令：把 title 的電流導向牆上的 Title 格子」\n",[38,3719,3720],{"class":40,"line":244},[38,3721,3722],{},"        this.Title = title; \n",[38,3724,3725],{"class":40,"line":249},[38,3726,264],{},[38,3728,3729],{"class":40,"line":255},[38,3730,270],{},[11,3732,3503],{},[387,3734,3735],{"start":191},[125,3736,3737],{},"為什麼class沒有static標籤也住在heap-static當中?",[122,3739,3740,3743],{},[125,3741,3742],{},"因為 「類別（Class）本身」不需要標籤，它出生就自帶最頂級的 VIP 通行證！",[125,3744,3745],{},"class 的本質就是「靜態設計圖」",[28,3747,3751],{"className":3748,"code":3749,"language":3750,"meta":33,"style":33},"language-plaintext! shiki shiki-themes github-light github-dark","【 🎬 記憶體大連動三幕劇 】\n\n 🟢 第一幕：程式開車通電 (Heap-Static 誕生)\n   [Heap-Static] ──載入 WorkItem 藍圖與 currentID ──➔ 萬事俱備\n                          │\n 🔵 第二幕：執行 new WorkItem() (Stack 點火)\n   [Stack] ───────➔ 壓入建構子工兵框架，手持 \"0\" 與 \"Default title\" 指令\n                          │\n 🟣 第三幕：工兵動工 (Heap-Heap 灌漿)\n   [Heap-Heap] ────➔ 挖出實體房間，由 Stack 電流將值寫入 ID 與 Title 房間\n","plaintext!",[35,3752,3753,3758,3762,3767,3772,3777,3782,3787,3791,3796],{"__ignoreMap":33},[38,3754,3755],{"class":40,"line":41},[38,3756,3757],{},"【 🎬 記憶體大連動三幕劇 】\n",[38,3759,3760],{"class":40,"line":47},[38,3761,194],{"emptyLinePlaceholder":70},[38,3763,3764],{"class":40,"line":62},[38,3765,3766],{}," 🟢 第一幕：程式開車通電 (Heap-Static 誕生)\n",[38,3768,3769],{"class":40,"line":173},[38,3770,3771],{},"   [Heap-Static] ──載入 WorkItem 藍圖與 currentID ──➔ 萬事俱備\n",[38,3773,3774],{"class":40,"line":179},[38,3775,3776],{},"                          │\n",[38,3778,3779],{"class":40,"line":185},[38,3780,3781],{}," 🔵 第二幕：執行 new WorkItem() (Stack 點火)\n",[38,3783,3784],{"class":40,"line":191},[38,3785,3786],{},"   [Stack] ───────➔ 壓入建構子工兵框架，手持 \"0\" 與 \"Default title\" 指令\n",[38,3788,3789],{"class":40,"line":197},[38,3790,3776],{},[38,3792,3793],{"class":40,"line":203},[38,3794,3795],{}," 🟣 第三幕：工兵動工 (Heap-Heap 灌漿)\n",[38,3797,3798],{"class":40,"line":209},[38,3799,3800],{},"   [Heap-Heap] ────➔ 挖出實體房間，由 Stack 電流將值寫入 ID 與 Title 房間\n",[11,3802,3503],{},[387,3804,3805],{"start":197},[125,3806,3807],{},"那struct是住在stack他也是住在heap-static嗎?",[122,3809,3810,3813],{},[125,3811,3812],{},"Struct 的「設計圖」住哪裡？➔ 【Heap-Static】",[125,3814,3815,3816],{},"Struct 的「肉身實體」住哪裡？➔ 【看它寄生在誰身上！】\n",[122,3817,3818,3821],{},[125,3819,3820],{},"class 很高傲，不管被誰呼叫，都一定要在 Heap-Heap 圈地蓋獨立大樓，別人只能拿指針（Pointer）指著它。",[125,3822,3823,3824],{},"struct（值型別）非常卑微，它沒有自己獨立的大樓（沒有物件車頭、沒有指針指著它）。它的物理特性是：「宿主（Container）住哪裡，它就原地寄生在哪裡！」\n",[122,3825,3826,3829,3832],{},[125,3827,3828],{},"宿主是「方法工兵」：你寫在方法肚子裡當區域變數 ➔ 宿主在 【Stack】，它就直接嵌在 Stack 的格子裡。",[125,3830,3831],{},"宿主是「Class大樓」：你寫在 Class 裡面當成員屬性 ➔ 宿主在 【Heap-Heap】，它就直接變成了大樓牆壁的一部分，死死躺在 Heap-Heap 裡。",[125,3833,3834],{},"宿主是「Static標籤」：你把它宣告成靜態成員 ➔ 宿主在 【Heap-Static】，它就直接焊在 Heap-Static 的設計圖旁邊。",[11,3836,3837],{},"=======================",[20,3839,3841],{"id":3840},"總結","總結:",[387,3843,3844,3847],{},[125,3845,3846],{},"方法與屬性 ➔ 走的是 「基因血脈鏈」，代代相傳，只要沒被 sealed（封印），孫子就可以一路啃老啃到最上層。",[125,3848,3849],{},"建構子 ➔ 走的是 「每代獨立的剪綵大門」，每個人名字都不同，無法繼承，只能在自己開門時，在幕後拉一條隱密通道（Constructor Chaining）去請老爸的推進器幫忙點火。",[272,3851],{},[85,3853,3855],{"id":3854},"heapstack的無殼理論","heap\u002Fstack的無殼理論",[20,3857,3858],{"id":3858},"值型別的深層真相",[11,3860,3861],{},"前面關於值型別都是說他是一筆真實的資料保存在stack中，可以更快速的存取，看似比參考型別這種之神資料保存在heap，在stack僅有位址(門牌)更基現代化，但其實不然:",[122,3863,3864,3872,3891],{},[125,3865,3866,3867],{},"本質是「沒有指針外殼的純資料」",[122,3868,3869],{},[125,3870,3871],{},"它不像 Class 擁有高傲的 VIP 外殼（沒有 Object Header 和 Method Table Pointer）。它全身光溜溜的，就是純粹的二進位資料顆粒（例如 int 是 4 Bytes、TimeSpan 是 8 Bytes）。",[125,3873,3874,3875],{},"因為沒有殼，它絕對無法「獨立」在 Heap-Heap 區開戶",[122,3876,3877],{},[125,3878,3879,3880],{},"它是一堆沒有腳的樂高積木，它的物理居住地址完全取決於它抱到誰的大腿（宿主）：\n",[122,3881,3882,3885,3888],{},[125,3883,3884],{},"孤身一人（方法內的區域變數） ➔ 只能肉身躺在 Stack 的臨時工作檯上。",[125,3886,3887],{},"抱到大腿（Class 內部的成員屬性） ➔ 隨著 Class 大樓一起嵌在 Heap-Heap 的實體牆壁裡。",[125,3889,3890],{},"自帶標籤（加了 static 關鍵字） ➔ 直接焊在 Heap-Static 的設計圖旁邊。",[125,3892,3893,3894],{},"觸發硬體大坑：裝箱（Boxing）的代價",[122,3895,3896],{},[125,3897,3898],{},"當你強迫一個無殼的值型別，非要硬生生把它塞給 object 或是介面（Interface）時，系統為了讓它能獨立待在 Heap-Heap，必須在幕後手工幫它打造一個「臨時的塑膠殼」。這個裝箱動作會劇烈消耗 CPU 算力，並在 Heap 區產生記憶體碎片。",[20,3900,3902],{"id":3901},"_1-為什麼class沒有static標籤也住在heap-static當中","1. 為什麼class沒有static標籤也住在heap-static當中?",[122,3904,3905,3907],{},[125,3906,3742],{},[125,3908,3745],{},[28,3910,3911],{"className":3748,"code":3749,"language":3750,"meta":33,"style":33},[35,3912,3913,3917,3921,3925,3929,3933,3937,3941,3945,3949],{"__ignoreMap":33},[38,3914,3915],{"class":40,"line":41},[38,3916,3757],{},[38,3918,3919],{"class":40,"line":47},[38,3920,194],{"emptyLinePlaceholder":70},[38,3922,3923],{"class":40,"line":62},[38,3924,3766],{},[38,3926,3927],{"class":40,"line":173},[38,3928,3771],{},[38,3930,3931],{"class":40,"line":179},[38,3932,3776],{},[38,3934,3935],{"class":40,"line":185},[38,3936,3781],{},[38,3938,3939],{"class":40,"line":191},[38,3940,3786],{},[38,3942,3943],{"class":40,"line":197},[38,3944,3776],{},[38,3946,3947],{"class":40,"line":203},[38,3948,3795],{},[38,3950,3951],{"class":40,"line":209},[38,3952,3800],{},[11,3954,3955],{},"===================",[20,3957,3959],{"id":3958},"_2-那struct是住在stack他也是住在heap-static嗎","2. 那struct是住在stack他也是住在heap-static嗎?",[122,3961,3962,3964],{},[125,3963,3812],{},[125,3965,3815,3966],{},[122,3967,3968,3970],{},[125,3969,3820],{},[125,3971,3823,3972],{},[122,3973,3974,3976,3978],{},[125,3975,3828],{},[125,3977,3831],{},[125,3979,3834],{},[11,3981,3955],{},[20,3983,3985],{"id":3984},"_3-c-的宇宙大一統萬物皆自-object-出","3. C# 的宇宙大一統：萬物皆自 object 出",[11,3987,3988],{},"微軟架構師立下了一條鐵律：全宇宙不論是多高傲的 class，還是多卑微的 struct（如 int、bool），它們往上追溯無數代，共同的始祖只有一個，叫做 System.Object（關鍵字小寫的 object）。",[28,3990,3992],{"className":1649,"code":3991,"language":1651,"meta":33,"style":33},"👑 object (System.Object) ➔ 它是全宇宙最頂層的 Class！\n                                 │\n         ┌───────────────────────┴───────────────────────┐\n         ▼                                               ▼\n【參考型別流派 (Reference Types)】             【值型別流派 (Value Types)】\n   • 所有自訂的普通 class                       • 核心中樞：System.ValueType (也是一隻 Class)\n   • 所有的 string、陣列、介面                             │\n                                                         ▼\n                                               • 所有的 struct (如 Point)\n                                               • 所有的內建基本型別 (int, bool, TimeSpan)\n",[35,3993,3994,3999,4004,4009,4014,4019,4024,4029,4034,4039],{"__ignoreMap":33},[38,3995,3996],{"class":40,"line":41},[38,3997,3998],{},"👑 object (System.Object) ➔ 它是全宇宙最頂層的 Class！\n",[38,4000,4001],{"class":40,"line":47},[38,4002,4003],{},"                                 │\n",[38,4005,4006],{"class":40,"line":62},[38,4007,4008],{},"         ┌───────────────────────┴───────────────────────┐\n",[38,4010,4011],{"class":40,"line":173},[38,4012,4013],{},"         ▼                                               ▼\n",[38,4015,4016],{"class":40,"line":179},[38,4017,4018],{},"【參考型別流派 (Reference Types)】             【值型別流派 (Value Types)】\n",[38,4020,4021],{"class":40,"line":185},[38,4022,4023],{},"   • 所有自訂的普通 class                       • 核心中樞：System.ValueType (也是一隻 Class)\n",[38,4025,4026],{"class":40,"line":191},[38,4027,4028],{},"   • 所有的 string、陣列、介面                             │\n",[38,4030,4031],{"class":40,"line":197},[38,4032,4033],{},"                                                         ▼\n",[38,4035,4036],{"class":40,"line":203},[38,4037,4038],{},"                                               • 所有的 struct (如 Point)\n",[38,4040,4041],{"class":40,"line":209},[38,4042,4043],{},"                                               • 所有的內建基本型別 (int, bool, TimeSpan)\n",[11,4045,3955],{},[20,4047,4049],{"id":4048},"_4-無殼的-struct-怎麼去繼承身為-class-的-object","4. 無殼的 struct 怎麼去繼承身為 Class 的 object？",[11,4051,4052],{},"這是 C# 語言設計史上最偉大的「雙面人設計」，用來解決「名義上的血緣」與「記憶體長相」的物理衝突：",[608,4054,4056],{"id":4055},"a-在日常生活裡未裝箱狀態","A. 在日常生活裡（未裝箱狀態）",[11,4058,4059,4060,4063,4064,4067],{},"當宣告 ",[35,4061,4062],{},"int age = 25;"," 或者",[35,4065,4066],{},"TimeSpan time = new TimeSpan();","時：",[122,4069,4070,4073],{},[125,4071,4072],{},"追求極致的快取效率與零 GC 壓力 -> C# 會完全隱瞞它繼承 object 的事實",[125,4074,4075],{},"在記憶體的物理世界裡，他就是沒有任何外殼的二進位資料，沒有任何地方長得像 object",[11,4077,4078],{},"++++++++++++++",[608,4080,4082],{"id":4081},"b-世界線交會當物理矛盾當場炸開","B. 世界線交會：當物理矛盾當場炸開",[11,4084,4085],{},"當你忽然手癢，寫出這行程式碼，強迫「物理上沒殼的平民」去坐「名義上老爸的皇帝位」時：",[28,4087,4089],{"className":1794,"code":4088,"language":1796,"meta":33,"style":33},"object obj = age; \u002F\u002F 💥 物理衝突點\n",[35,4090,4091],{"__ignoreMap":33},[38,4092,4093],{"class":40,"line":41},[38,4094,4088],{},[122,4096,4097,4100],{},[125,4098,4099],{},"左邊的 object obj：在 【Stack 區】 宣告了一個空皮包（指針變數），它規定自己只能收納 【Heap-Heap 區的大樓門牌（地址）】，且要求大樓門口必須有標準外殼結構（Object Header）。",[125,4101,4102],{},"右邊的 age：躺在 【Stack 區】 的純資料，沒有門牌地址讓別人追蹤，更沒有外殼結構。\n皮包無法直接綁定一個沒殼且沒門牌的資料，為了不讓 CPU 順著指針衝進 Stack 撞牆，CLR 必須強制啟動 Boxing 施工隊進行物理轉譯。",[608,4104,4106],{"id":4105},"c-裝箱boxing的四步施工作業","C. 裝箱（Boxing）的四步施工作業",[122,4108,4109,4112,4115,4118],{},[125,4110,4111],{},"區圈地：CPU 衝進 【Heap-Heap】 區大喊：「快給我撥一塊全新大樓的空間！」",[125,4113,4114],{},"蓋出標準外殼：在這塊地上焊上 System.Object 規格的指針外殼（Object Header + Method Table 指針），符合 obj 皮包的挑剔胃口。",[125,4116,4117],{},"複製肉身：把 Stack 區 age 肚子裡的 25 影印一份，塞進 Heap-Heap 這棟新外殼大樓的客廳裡。",[125,4119,4120],{},"發放門牌並收納：新大樓在 Heap-Heap 產生了合法的實體地址門牌（例如 0x9FFF），最後把這個門牌寫進 Stack 區的 obj 皮包裡。",[11,4122,3955],{},[56,4124,58],{},{"title":33,"searchDepth":47,"depth":47,"links":4126},[4127,4131],{"id":3002,"depth":47,"text":3002,"children":4128},[4129,4130],{"id":3479,"depth":62,"text":3479},{"id":3840,"depth":62,"text":3841},{"id":3854,"depth":47,"text":3855,"children":4132},[4133,4134,4135,4136,4137],{"id":3858,"depth":62,"text":3858},{"id":3901,"depth":62,"text":3902},{"id":3958,"depth":62,"text":3959},{"id":3984,"depth":62,"text":3985},{"id":4048,"depth":62,"text":4049},"2026-08-09","\u002F\u002F WorkItem implicitly inherits from the Object class.",{},"\u002Fnotes\u002Fsync\u002Fcsharp-02",{"title":2997,"description":4139},"C#\u002F易混淆概念\u002F易混淆概念02.md","notes\u002Fsync\u002Fcsharp-易混淆概念-易混淆概念02",[76,886],"aUkXACHJgMk2oqo8LYLFiKqd0VRueGre-u64DvAdE5E",{"id":4148,"title":4149,"body":4150,"category":64,"date":4138,"description":4159,"draft":67,"extension":68,"meta":4486,"navigation":70,"path":4487,"seo":4488,"sourcePath":4489,"stem":4490,"tags":4491,"__hash__":4492},"notes\u002Fnotes\u002Fsync\u002Fcsharp-易混淆概念-易混淆概念03.md","易混淆概念03",{"type":8,"value":4151,"toc":4477},[4152,4155,4160,4163,4167,4170,4173,4177,4180,4183,4187,4192,4196,4199,4264,4375,4378,4382,4385,4388,4458,4460,4464,4468,4473,4475],[85,4153,4154],{"id":4154},"主要建構子與建構子",[89,4156,4157],{},[11,4158,4159],{},"它是為了「依賴注入（DI）」而量身打造的特化武器！",[20,4161,4162],{"id":4162},"建構子類型",[608,4164,4166],{"id":4165},"服務型大樓service-repository-controller-100-全面換成主要建構子","服務型大樓（Service \u002F Repository \u002F Controller）➔ 🎯 100% 全面換成主要建構子",[11,4168,4169],{},"這類大樓（像是 EfQuoteRepository）一輩子只需要一種出生方式——就是把 DbContext 塞進去。它們肚子裡完全不需要寫 if 判斷，全心全意做依賴注入。這時候用主要建構子，程式碼精簡到像詩一樣。",[11,4171,4172],{},"++++++",[608,4174,4176],{"id":4175},"資料型大樓domain-entities-models-dto-️-保持傳統建構子","資料型大樓（Domain Entities \u002F Models \u002F DTO）➔ 🛡️ 保持傳統建構子",[11,4178,4179],{},"這類大樓（像是 Order、User）肚子裡裝的是商業靈魂，需要防禦欄位不能為 null、數字不能小於零、或者需要多種出生參數。這時候，老老實實用傳統寫法，才是最安全的防守。",[11,4181,4182],{},"========",[20,4184,4186],{"id":4185},"主要建構子能夠完全建構子嗎","主要建構子能夠完全建構子嗎?",[89,4188,4189],{},[11,4190,4191],{},"不行，主要建構子（Primary Constructor）絕對沒辦法「全面取代」原有的建構子寫法。",[608,4193,4195],{"id":4194},"限制一它沒辦法單獨玩多載overloading多個建構子","限制一：它沒辦法單獨玩「多載（Overloading）」（多個建構子）",[11,4197,4198],{},"在物件導向的世界裡，我們常常需要一棟大樓有「多種不同的出生方式」。\n例如：建立一個 Member（會員）物件，有時候我們需要傳入完整的資料；但有時候（像單元測試）我們只想建立一個空物件。",[28,4200,4202],{"className":1794,"code":4201,"language":1796,"meta":33,"style":33},"\u002F\u002F 🟢 這是主要建構子（大房）\npublic class Member(string name, int age)\n{\n    public string Name { get; set; } = name;\n    public int Age { get; set; } = age;\n\n    \u002F\u002F 🔴 驚悚點：你想寫一個「不用傳參數」的傳統建構子？\n    \u002F\u002F 你必須用 : this(...) 強制去呼叫主要建構子，幫它填預設值！不呼叫編譯器就直接把你開除！\n    public Member() : this(\"神秘嘉賓\", 18) \n    {\n        \u002F\u002F 這裡才能寫別的初始邏輯\n    }\n}\n",[35,4203,4204,4209,4214,4218,4223,4228,4232,4237,4242,4247,4251,4256,4260],{"__ignoreMap":33},[38,4205,4206],{"class":40,"line":41},[38,4207,4208],{},"\u002F\u002F 🟢 這是主要建構子（大房）\n",[38,4210,4211],{"class":40,"line":47},[38,4212,4213],{},"public class Member(string name, int age)\n",[38,4215,4216],{"class":40,"line":62},[38,4217,165],{},[38,4219,4220],{"class":40,"line":173},[38,4221,4222],{},"    public string Name { get; set; } = name;\n",[38,4224,4225],{"class":40,"line":179},[38,4226,4227],{},"    public int Age { get; set; } = age;\n",[38,4229,4230],{"class":40,"line":185},[38,4231,194],{"emptyLinePlaceholder":70},[38,4233,4234],{"class":40,"line":191},[38,4235,4236],{},"    \u002F\u002F 🔴 驚悚點：你想寫一個「不用傳參數」的傳統建構子？\n",[38,4238,4239],{"class":40,"line":197},[38,4240,4241],{},"    \u002F\u002F 你必須用 : this(...) 強制去呼叫主要建構子，幫它填預設值！不呼叫編譯器就直接把你開除！\n",[38,4243,4244],{"class":40,"line":203},[38,4245,4246],{},"    public Member() : this(\"神秘嘉賓\", 18) \n",[38,4248,4249],{"class":40,"line":209},[38,4250,176],{},[38,4252,4253],{"class":40,"line":215},[38,4254,4255],{},"        \u002F\u002F 這裡才能寫別的初始邏輯\n",[38,4257,4258],{"class":40,"line":221},[38,4259,264],{},[38,4261,4262],{"class":40,"line":227},[38,4263,270],{},[28,4265,4267],{"className":1794,"code":4266,"language":1796,"meta":33,"style":33},"public class Symbol\n{\n    public string Code { get; set; }\n    public string Name { get; set; }\n\n    \u002F\u002F 🚪 獨立大門 A：給證交所 API 用（天天正常出生）\n    public Symbol(string code, string name)\n    {\n        this.Code = code;\n        this.Name = name;\n        Console.WriteLine(\"從網路 API 正常創建股票物件。\");\n    }\n\n    \u002F\u002F 🚪 獨立大門 B：給快取或測試用（從一段加密的 JSON 字串直接還原）\n    \u002F\u002F 💡 瞧！這兩個建構子各走各的路，誰也不用去呼叫誰，內部邏輯完全平行的！\n    public Symbol(string encryptedJson)\n    {\n        var data = MyCryptoTool.DecryptAndParse(encryptedJson); \u002F\u002F 解密大數據\n        this.Code = data.Code;\n        this.Name = data.Name;\n        Console.WriteLine(\"從加密檔案秘密復活股票物件！\");\n    }\n}\n",[35,4268,4269,4274,4278,4283,4287,4291,4296,4301,4305,4310,4315,4320,4324,4328,4333,4338,4343,4347,4352,4357,4362,4367,4371],{"__ignoreMap":33},[38,4270,4271],{"class":40,"line":41},[38,4272,4273],{},"public class Symbol\n",[38,4275,4276],{"class":40,"line":47},[38,4277,165],{},[38,4279,4280],{"class":40,"line":62},[38,4281,4282],{},"    public string Code { get; set; }\n",[38,4284,4285],{"class":40,"line":173},[38,4286,2084],{},[38,4288,4289],{"class":40,"line":179},[38,4290,194],{"emptyLinePlaceholder":70},[38,4292,4293],{"class":40,"line":185},[38,4294,4295],{},"    \u002F\u002F 🚪 獨立大門 A：給證交所 API 用（天天正常出生）\n",[38,4297,4298],{"class":40,"line":191},[38,4299,4300],{},"    public Symbol(string code, string name)\n",[38,4302,4303],{"class":40,"line":197},[38,4304,176],{},[38,4306,4307],{"class":40,"line":203},[38,4308,4309],{},"        this.Code = code;\n",[38,4311,4312],{"class":40,"line":209},[38,4313,4314],{},"        this.Name = name;\n",[38,4316,4317],{"class":40,"line":215},[38,4318,4319],{},"        Console.WriteLine(\"從網路 API 正常創建股票物件。\");\n",[38,4321,4322],{"class":40,"line":221},[38,4323,264],{},[38,4325,4326],{"class":40,"line":227},[38,4327,194],{"emptyLinePlaceholder":70},[38,4329,4330],{"class":40,"line":232},[38,4331,4332],{},"    \u002F\u002F 🚪 獨立大門 B：給快取或測試用（從一段加密的 JSON 字串直接還原）\n",[38,4334,4335],{"class":40,"line":238},[38,4336,4337],{},"    \u002F\u002F 💡 瞧！這兩個建構子各走各的路，誰也不用去呼叫誰，內部邏輯完全平行的！\n",[38,4339,4340],{"class":40,"line":244},[38,4341,4342],{},"    public Symbol(string encryptedJson)\n",[38,4344,4345],{"class":40,"line":249},[38,4346,176],{},[38,4348,4349],{"class":40,"line":255},[38,4350,4351],{},"        var data = MyCryptoTool.DecryptAndParse(encryptedJson); \u002F\u002F 解密大數據\n",[38,4353,4354],{"class":40,"line":261},[38,4355,4356],{},"        this.Code = data.Code;\n",[38,4358,4359],{"class":40,"line":267},[38,4360,4361],{},"        this.Name = data.Name;\n",[38,4363,4364],{"class":40,"line":1264},[38,4365,4366],{},"        Console.WriteLine(\"從加密檔案秘密復活股票物件！\");\n",[38,4368,4369],{"class":40,"line":1269},[38,4370,264],{},[38,4372,4373],{"class":40,"line":3116},[38,4374,270],{},[11,4376,4377],{},"+++++++",[608,4379,4381],{"id":4380},"限制二它沒有肚子body可以寫複雜的防禦邏輯","❌ 限制二：它沒有「肚子（Body）」可以寫複雜的防禦邏輯",[11,4383,4384],{},"傳統建構子最厲害的地方，是它自帶一個大括號 { ... } 的肉身肚子。我們可以在資料剛進大門、還沒塞給屬性之前，在肚子裡進行「安檢」或「大數據加工」。",[11,4386,4387],{},"主要建構子在 C# 12 裡是沒有肚子的！它只能做簡單的指派。",[28,4389,4391],{"className":1794,"code":4390,"language":1796,"meta":33,"style":33},"\u002F\u002F 💻 傳統寫法：肚子裡可以翻江倒海\npublic class Order\n{\n    public decimal Price { get; private set; }\n\n    public Order(decimal price)\n    {\n        \u002F\u002F 🛡️ 防禦性程式碼：進門先安檢，不合格當場噴錯！\n        if (price \u003C= 0) \n            throw new ArgumentException(\"價格不能是搶劫價！\");\n            \n        this.Price = price * 0.9m; \u002F\u002F 順便打個九折\n    }\n}\n",[35,4392,4393,4398,4403,4407,4412,4416,4421,4425,4430,4435,4440,4445,4450,4454],{"__ignoreMap":33},[38,4394,4395],{"class":40,"line":41},[38,4396,4397],{},"\u002F\u002F 💻 傳統寫法：肚子裡可以翻江倒海\n",[38,4399,4400],{"class":40,"line":47},[38,4401,4402],{},"public class Order\n",[38,4404,4405],{"class":40,"line":62},[38,4406,165],{},[38,4408,4409],{"class":40,"line":173},[38,4410,4411],{},"    public decimal Price { get; private set; }\n",[38,4413,4414],{"class":40,"line":179},[38,4415,194],{"emptyLinePlaceholder":70},[38,4417,4418],{"class":40,"line":185},[38,4419,4420],{},"    public Order(decimal price)\n",[38,4422,4423],{"class":40,"line":191},[38,4424,176],{},[38,4426,4427],{"class":40,"line":197},[38,4428,4429],{},"        \u002F\u002F 🛡️ 防禦性程式碼：進門先安檢，不合格當場噴錯！\n",[38,4431,4432],{"class":40,"line":203},[38,4433,4434],{},"        if (price \u003C= 0) \n",[38,4436,4437],{"class":40,"line":209},[38,4438,4439],{},"            throw new ArgumentException(\"價格不能是搶劫價！\");\n",[38,4441,4442],{"class":40,"line":215},[38,4443,4444],{},"            \n",[38,4446,4447],{"class":40,"line":221},[38,4448,4449],{},"        this.Price = price * 0.9m; \u002F\u002F 順便打個九折\n",[38,4451,4452],{"class":40,"line":227},[38,4453,264],{},[38,4455,4456],{"class":40,"line":232},[38,4457,270],{},[272,4459],{},[85,4461,4463],{"id":4462},"比較資料庫-ef-core-與-typeorm","比較資料庫 EF Core 與 TypeOrm",[20,4465,4467],{"id":4466},"ef-core","EF Core",[89,4469,4470],{},[11,4471,4472],{},"約定優於配置",[272,4474],{},[56,4476,58],{},{"title":33,"searchDepth":47,"depth":47,"links":4478},[4479,4483],{"id":4154,"depth":47,"text":4154,"children":4480},[4481,4482],{"id":4162,"depth":62,"text":4162},{"id":4185,"depth":62,"text":4186},{"id":4462,"depth":47,"text":4463,"children":4484},[4485],{"id":4466,"depth":62,"text":4467},{},"\u002Fnotes\u002Fsync\u002Fcsharp-03",{"title":4149,"description":4159},"C#\u002F易混淆概念\u002F易混淆概念03.md","notes\u002Fsync\u002Fcsharp-易混淆概念-易混淆概念03",[76,886],"RYybNrYxvXY6W2KO7zcP63SRBLWsO15KZ28Jud1Nnh4",{"id":4494,"title":4495,"body":4496,"category":64,"date":4138,"description":4505,"draft":67,"extension":68,"meta":4949,"navigation":70,"path":2544,"seo":4950,"sourcePath":4951,"stem":4952,"tags":4953,"__hash__":4955},"notes\u002Fnotes\u002Fsync\u002Fcsharp-物件-物件-01.md","物件 01",{"type":8,"value":4497,"toc":4943},[4498,4501,4506,4509,4512,4525,4528,4532,4537,4567,4606,4616,4639,4645,4648,4651,4654,4662,4666,4710,4713,4717,4756,4758,4762,4765,4770,4845,4850,4853,4881,4884,4939,4941],[85,4499,4500],{"id":4500},"建立類型的實例",[89,4502,4503],{},[11,4504,4505],{},"物件導向（OOP）的核心靈魂是「封裝與實體（Instance）」——每個人有每個人的狀態",[20,4507,4508],{"id":4508},"與靜態類型差異",[11,4510,4511],{},"注意！我們前面講的『蓋大樓、傳名片、全肉身影印』這套生命週期，只要遇到『靜態（static）』這兩個字，全部當場失效！它是另一個世界的生物！",[122,4513,4514],{},[125,4515,4516,4517],{},"一個世界兩套法則：蓋大樓 vs. 太陽\n",[122,4518,4519,4522],{},[125,4520,4521],{},"普通物件 \u002F 類別（我們之前聊的所有東西）：\n它是「建築藍圖」。你必須親手呼叫 new，才能在 Heap 或 Stack 蓋出實體大樓。你可以蓋 100 棟，每棟大樓的門牌（地址）都不同，裡面的住戶（資料）也互不干涉。",[125,4523,4524],{},"靜態類別 \u002F 靜態成員（static）：\n它是天上的「太陽」。全宇宙（整個專案應用程式）就只有這麼一個，而且絕對不需要、也沒辦法用 new 去蓋它。所有人只要抬頭（直接用類別名稱），就能共享同一個太陽。",[11,4526,4527],{},"+++++++++++++++++",[608,4529,4531],{"id":4530},"物理真相","物理真相：",[89,4533,4534],{},[11,4535,4536],{},"static 徹底打破了 Heap 和 Stack 的遊戲規則",[387,4538,4539,4556],{},[125,4540,4541,4542],{},"它是「長生不老」的（Lifecycle）",[122,4543,4544,4550],{},[125,4545,4546,4549],{},[35,4547,4548],{},"普通物件","：用 new 誕生，沒人用了就被 GC 垃圾車載走，或者隨著函式結束在 Stack 蒸發。",[125,4551,4552,4555],{},[35,4553,4554],{},"靜態類型","：當你的程式（Web API 或是 App）一啟動的那一微秒，它就直接進駐記憶體裡一個特殊的「靜態儲存區（Static Storage）」。它不需要被 new 誕生，而且只要你的程式沒有關閉，它就永遠不死。",[125,4557,4558,4559],{},"它沒有「名片」的概念，它是「全域共享」的",[122,4560,4561,4564],{},[125,4562,4563],{},"record class 傳遞時是傳 8 位元組的名片。但 static 不一樣，你不需要傳遞它。",[125,4565,4566],{},"任何執行緒、任何函式，隨時隨地都能直接存取它。",[28,4568,4570],{"className":1794,"code":4569,"language":1796,"meta":33,"style":33},"\u002F\u002F 🏢 普通類別：必須 new 才能用\nvar p1 = new Product(\"iPhone\");\nvar p2 = new Product(\"iPad\"); \u002F\u002F p1 和 p2 是獨立的兩棟大樓\n\n\u002F\u002F ☀️ 靜態類別：不需要 new，全世界共享同一個\n\u002F\u002F 就像 Math.Sqrt() 或 Console.WriteLine()，你從來沒看過 new Console() 對吧？\nGlobalConfig.Version = \"v2.0\";\n",[35,4571,4572,4577,4582,4587,4591,4596,4601],{"__ignoreMap":33},[38,4573,4574],{"class":40,"line":41},[38,4575,4576],{},"\u002F\u002F 🏢 普通類別：必須 new 才能用\n",[38,4578,4579],{"class":40,"line":47},[38,4580,4581],{},"var p1 = new Product(\"iPhone\");\n",[38,4583,4584],{"class":40,"line":62},[38,4585,4586],{},"var p2 = new Product(\"iPad\"); \u002F\u002F p1 和 p2 是獨立的兩棟大樓\n",[38,4588,4589],{"class":40,"line":173},[38,4590,194],{"emptyLinePlaceholder":70},[38,4592,4593],{"class":40,"line":179},[38,4594,4595],{},"\u002F\u002F ☀️ 靜態類別：不需要 new，全世界共享同一個\n",[38,4597,4598],{"class":40,"line":185},[38,4599,4600],{},"\u002F\u002F 就像 Math.Sqrt() 或 Console.WriteLine()，你從來沒看過 new Console() 對吧？\n",[38,4602,4603],{"class":40,"line":191},[38,4604,4605],{},"GlobalConfig.Version = \"v2.0\";\n",[387,4607,4608],{"start":62},[125,4609,4610,4611],{},"多執行緒的「終極修羅場」\n",[122,4612,4613],{},[125,4614,4615],{},"如果你的靜態類別裡面，放了一個可以修改的變數（Mutable set）：",[28,4617,4619],{"className":1794,"code":4618,"language":1796,"meta":33,"style":33},"public static class GlobalCounter \n{\n    public static int Count = 0; \u002F\u002F 🚨 危險源頭！\n}\n",[35,4620,4621,4626,4630,4635],{"__ignoreMap":33},[38,4622,4623],{"class":40,"line":41},[38,4624,4625],{},"public static class GlobalCounter \n",[38,4627,4628],{"class":40,"line":47},[38,4629,165],{},[38,4631,4632],{"class":40,"line":62},[38,4633,4634],{},"    public static int Count = 0; \u002F\u002F 🚨 危險源頭！\n",[38,4636,4637],{"class":40,"line":173},[38,4638,270],{},[11,4640,4641,4642],{},"會導致所有調用都進來改動他的值，導致多條執行緒會同時、沒有隔離地去搶奪、修改同一個記憶體格子 => 引發 ",[15,4643,4644],{},"資料爭用（Race Condition）",[11,4646,4647],{},"================",[20,4649,4650],{"id":4650},"對象識別與值相等",[11,4652,4653],{},"在 C# 裡寫 A == B 的時候，你必須先搞清楚你到底是在「比對兩個人是不是同一個人（身分證號碼）」，還是在「比對這兩個人是不是長得一模一樣（複製人）」。",[122,4655,4656,4659],{},[125,4657,4658],{},"引用相等性 (Reference Equality)：看變數是不是代表記憶體中的「同一個實體」（比對門牌）。",[125,4660,4661],{},"值相等性 (Value Equality)：看物件肚子裡的「欄位數值是否相等」（比對 DNA）。",[608,4663,4665],{"id":4664},"_1-如果是參考型別類別-class陣列-array","1. 如果是「參考型別」（類別 Class、陣列 Array）",[28,4667,4669],{"className":1794,"code":4668,"language":1796,"meta":33,"style":33},"\u002F\u002F 宣告兩個一模一樣的陣列（陣列是參考型別喔！）\nint[] array1 = [1, 2, 3];\nint[] array2 = [1, 2, 3];\n\n\u002F\u002F 🚨 結果是 false！\n\u002F\u002F 因為它們是蓋在 Heap 上不同地址的兩棟建築物。\n\u002F\u002F 雖然肚子裡的數字都是 1, 2, 3，但對參考型別來說，門牌不同，就是「不相等」！\nConsole.WriteLine(array1 == array2);\n",[35,4670,4671,4676,4681,4686,4690,4695,4700,4705],{"__ignoreMap":33},[38,4672,4673],{"class":40,"line":41},[38,4674,4675],{},"\u002F\u002F 宣告兩個一模一樣的陣列（陣列是參考型別喔！）\n",[38,4677,4678],{"class":40,"line":47},[38,4679,4680],{},"int[] array1 = [1, 2, 3];\n",[38,4682,4683],{"class":40,"line":62},[38,4684,4685],{},"int[] array2 = [1, 2, 3];\n",[38,4687,4688],{"class":40,"line":173},[38,4689,194],{"emptyLinePlaceholder":70},[38,4691,4692],{"class":40,"line":179},[38,4693,4694],{},"\u002F\u002F 🚨 結果是 false！\n",[38,4696,4697],{"class":40,"line":185},[38,4698,4699],{},"\u002F\u002F 因為它們是蓋在 Heap 上不同地址的兩棟建築物。\n",[38,4701,4702],{"class":40,"line":191},[38,4703,4704],{},"\u002F\u002F 雖然肚子裡的數字都是 1, 2, 3，但對參考型別來說，門牌不同，就是「不相等」！\n",[38,4706,4707],{"class":40,"line":197},[38,4708,4709],{},"Console.WriteLine(array1 == array2);\n",[11,4711,4712],{},"++++++++++++++++",[608,4714,4716],{"id":4715},"_2-如果是實值型別結構體-struct","2. 如果是「實值型別」（結構體 Struct）",[28,4718,4720],{"className":1794,"code":4719,"language":1796,"meta":33,"style":33},"\u002F\u002F 假設 Person 是個 struct\nPerson p1 = new(\"Alex\", 9);\nPerson p2 = new(\"Alex\", 9);\n\n\u002F\u002F 🟢 結果是 true！\n\u002F\u002F 因為 Struct 天生就是比對「值是否相等」。只要肚子裡的名字跟年齡一樣，海關就判斷相等！\nConsole.WriteLine(p1.Equals(p2));\n",[35,4721,4722,4727,4732,4737,4741,4746,4751],{"__ignoreMap":33},[38,4723,4724],{"class":40,"line":41},[38,4725,4726],{},"\u002F\u002F 假設 Person 是個 struct\n",[38,4728,4729],{"class":40,"line":47},[38,4730,4731],{},"Person p1 = new(\"Alex\", 9);\n",[38,4733,4734],{"class":40,"line":62},[38,4735,4736],{},"Person p2 = new(\"Alex\", 9);\n",[38,4738,4739],{"class":40,"line":173},[38,4740,194],{"emptyLinePlaceholder":70},[38,4742,4743],{"class":40,"line":179},[38,4744,4745],{},"\u002F\u002F 🟢 結果是 true！\n",[38,4747,4748],{"class":40,"line":185},[38,4749,4750],{},"\u002F\u002F 因為 Struct 天生就是比對「值是否相等」。只要肚子裡的名字跟年齡一樣，海關就判斷相等！\n",[38,4752,4753],{"class":40,"line":191},[38,4754,4755],{},"Console.WriteLine(p1.Equals(p2));\n",[11,4757,4712],{},[608,4759,4761],{"id":4760},"_3那-record呢","3.那 record呢?",[11,4763,4764],{},"record 的本質雖然是 Class（參考型別），但微軟在底層強行把它的比對邏輯，改造成跟 Struct 一樣的「值相等性（比對 DNA）」。它幫你打破了參考型別「只比門牌」的宿命！",[122,4766,4767],{},[125,4768,4769],{},"classRecord == structRecord 無法存在",[28,4771,4773],{"className":1794,"code":4772,"language":1796,"meta":33,"style":33},"\u002F\u002F 🏢 這是 Class 基底（活在 Heap 區）\npublic record ProductClass(string Name, decimal Price);\n\n\u002F\u002F 📦 這是 Struct 基底（活在 Stack 區）\npublic record struct ProductStruct(string Name, decimal Price);\n\n\u002F\u002F 呼叫時：\nvar p1 = new ProductClass(\"iPhone\", 30000);\nvar p2 = new ProductStruct(\"iPhone\", 30000);\n\n\u002F\u002F ❌ 這一行會直接跳紅字報錯，程式根本動不了！\nif (p1 == p2) \n{\n    \u002F\u002F 編譯器會說：運算子 '==' 無法套用於 'ProductClass' 與 'ProductStruct'\n}\n",[35,4774,4775,4780,4785,4789,4794,4799,4803,4808,4813,4818,4822,4827,4832,4836,4841],{"__ignoreMap":33},[38,4776,4777],{"class":40,"line":41},[38,4778,4779],{},"\u002F\u002F 🏢 這是 Class 基底（活在 Heap 區）\n",[38,4781,4782],{"class":40,"line":47},[38,4783,4784],{},"public record ProductClass(string Name, decimal Price);\n",[38,4786,4787],{"class":40,"line":62},[38,4788,194],{"emptyLinePlaceholder":70},[38,4790,4791],{"class":40,"line":173},[38,4792,4793],{},"\u002F\u002F 📦 這是 Struct 基底（活在 Stack 區）\n",[38,4795,4796],{"class":40,"line":179},[38,4797,4798],{},"public record struct ProductStruct(string Name, decimal Price);\n",[38,4800,4801],{"class":40,"line":185},[38,4802,194],{"emptyLinePlaceholder":70},[38,4804,4805],{"class":40,"line":191},[38,4806,4807],{},"\u002F\u002F 呼叫時：\n",[38,4809,4810],{"class":40,"line":197},[38,4811,4812],{},"var p1 = new ProductClass(\"iPhone\", 30000);\n",[38,4814,4815],{"class":40,"line":203},[38,4816,4817],{},"var p2 = new ProductStruct(\"iPhone\", 30000);\n",[38,4819,4820],{"class":40,"line":209},[38,4821,194],{"emptyLinePlaceholder":70},[38,4823,4824],{"class":40,"line":215},[38,4825,4826],{},"\u002F\u002F ❌ 這一行會直接跳紅字報錯，程式根本動不了！\n",[38,4828,4829],{"class":40,"line":221},[38,4830,4831],{},"if (p1 == p2) \n",[38,4833,4834],{"class":40,"line":227},[38,4835,165],{},[38,4837,4838],{"class":40,"line":232},[38,4839,4840],{},"    \u002F\u002F 編譯器會說：運算子 '==' 無法套用於 'ProductClass' 與 'ProductStruct'\n",[38,4842,4843],{"class":40,"line":238},[38,4844,270],{},[122,4846,4847],{},[125,4848,4849],{},"解法:硬核拆包，逐欄手動比對 \u002F 架構大統一，把其中一個「變身」",[11,4851,4852],{},"姿勢一：硬核拆包，逐欄手動比對",[28,4854,4856],{"className":1794,"code":4855,"language":1796,"meta":33,"style":33},"\u002F\u002F 🟢 這樣寫完全合法！因為是在比對字串跟數字\nif (p1.Name == p2.Name && p1.Price == p2.Price)\n{\n    Console.WriteLine(\"雖然基底不同，但肚子裡的資料長得一模一樣！\");\n}\n",[35,4857,4858,4863,4868,4872,4877],{"__ignoreMap":33},[38,4859,4860],{"class":40,"line":41},[38,4861,4862],{},"\u002F\u002F 🟢 這樣寫完全合法！因為是在比對字串跟數字\n",[38,4864,4865],{"class":40,"line":47},[38,4866,4867],{},"if (p1.Name == p2.Name && p1.Price == p2.Price)\n",[38,4869,4870],{"class":40,"line":62},[38,4871,165],{},[38,4873,4874],{"class":40,"line":173},[38,4875,4876],{},"    Console.WriteLine(\"雖然基底不同，但肚子裡的資料長得一模一樣！\");\n",[38,4878,4879],{"class":40,"line":179},[38,4880,270],{},[11,4882,4883],{},"姿勢二：架構大統一，把其中一個「變身」",[28,4885,4887],{"className":1794,"code":4886,"language":1796,"meta":33,"style":33},"\u002F\u002F 在 Struct 內部寫一個可以把自己複製成 Class 版的方法\npublic record struct ProductStruct(string Name, decimal Price)\n{\n    public ProductClass ToClass() => new ProductClass(Name, Price);\n}\n\n\u002F\u002F 💡 比較時，先把 p2 變身成 Class，這樣兩邊都是 Class 大樓，就能大膽用 == 了！\nif (p1 == p2.ToClass()) \n{\n    Console.WriteLine(\"完美通關！\");\n}\n",[35,4888,4889,4894,4899,4903,4908,4912,4916,4921,4926,4930,4935],{"__ignoreMap":33},[38,4890,4891],{"class":40,"line":41},[38,4892,4893],{},"\u002F\u002F 在 Struct 內部寫一個可以把自己複製成 Class 版的方法\n",[38,4895,4896],{"class":40,"line":47},[38,4897,4898],{},"public record struct ProductStruct(string Name, decimal Price)\n",[38,4900,4901],{"class":40,"line":62},[38,4902,165],{},[38,4904,4905],{"class":40,"line":173},[38,4906,4907],{},"    public ProductClass ToClass() => new ProductClass(Name, Price);\n",[38,4909,4910],{"class":40,"line":179},[38,4911,270],{},[38,4913,4914],{"class":40,"line":185},[38,4915,194],{"emptyLinePlaceholder":70},[38,4917,4918],{"class":40,"line":191},[38,4919,4920],{},"\u002F\u002F 💡 比較時，先把 p2 變身成 Class，這樣兩邊都是 Class 大樓，就能大膽用 == 了！\n",[38,4922,4923],{"class":40,"line":197},[38,4924,4925],{},"if (p1 == p2.ToClass()) \n",[38,4927,4928],{"class":40,"line":203},[38,4929,165],{},[38,4931,4932],{"class":40,"line":209},[38,4933,4934],{},"    Console.WriteLine(\"完美通關！\");\n",[38,4936,4937],{"class":40,"line":215},[38,4938,270],{},[11,4940,4647],{},[56,4942,58],{},{"title":33,"searchDepth":47,"depth":47,"links":4944},[4945],{"id":4500,"depth":47,"text":4500,"children":4946},[4947,4948],{"id":4508,"depth":62,"text":4508},{"id":4650,"depth":62,"text":4650},{},{"title":4495,"description":4505},"C#\u002F物件\u002F物件 01.md","notes\u002Fsync\u002Fcsharp-物件-物件-01",[76,4954],"OOP","XlSyQGeAfS1wfLgWEkam66oojhaPNYdWUYrVf0TO8Lg",{"id":4957,"title":4958,"body":4959,"category":64,"date":4138,"description":4965,"draft":67,"extension":68,"meta":6291,"navigation":70,"path":4141,"seo":6292,"sourcePath":6293,"stem":6294,"tags":6295,"__hash__":6296},"notes\u002Fnotes\u002Fsync\u002Fcsharp-物件-物件-02-繼承.md","物件 02 繼承",{"type":8,"value":4960,"toc":6275},[4961,4966,4977,4981,4985,5016,5020,5055,5059,5063,5145,5149,5204,5208,5210,5214,5562,5698,5702,5792,5795,5799,5802,5871,5875,5877,5881,5896,5900,5914,5918,5929,5934,5937,5946,5960,5985,5999,6001,6005,6009,6012,6023,6027,6030,6041,6044,6046,6050,6063,6066,6068,6072,6096,6098,6102,6107,6133,6160,6162,6166,6223,6225,6229,6234,6261,6263,6265,6273],[89,4962,4963],{},[11,4964,4965],{},"繼承與封裝和多型一起，是面向對象程序設計三個主要特性之一",[122,4967,4968,4971,4974],{},[125,4969,4970],{},"繼承成員的 類別稱為基類，而繼承這些成員的類別稱為 衍生類別。",[125,4972,4973],{},"衍生類別只能有一個直接基類。",[125,4975,4976],{},"繼承是可轉移的。",[85,4978,4980],{"id":4979},"可轉移性","可轉移性?",[20,4982,4984],{"id":4983},"正確的理解垂直的傳遞性爺爺-爸爸-孫子","正確的理解：垂直的「傳遞性」（爺爺 ➔ 爸爸 ➔ 孫子）",[28,4986,4990],{"className":4987,"code":4988,"language":4989,"meta":33,"style":33},"language-Plaintext shiki shiki-themes github-light github-dark","ClassA (爺爺：留下一把劍)\n   ▼\nClassB (爸爸：繼承了劍，自己買了盾)\n   ▼\nClassC (孫子：繼承爸爸，自動「傳遞」獲得了劍和盾)\n","Plaintext",[35,4991,4992,4997,5002,5007,5011],{"__ignoreMap":33},[38,4993,4994],{"class":40,"line":41},[38,4995,4996],{},"ClassA (爺爺：留下一把劍)\n",[38,4998,4999],{"class":40,"line":47},[38,5000,5001],{},"   ▼\n",[38,5003,5004],{"class":40,"line":62},[38,5005,5006],{},"ClassB (爸爸：繼承了劍，自己買了盾)\n",[38,5008,5009],{"class":40,"line":173},[38,5010,5001],{},[38,5012,5013],{"class":40,"line":179},[38,5014,5015],{},"ClassC (孫子：繼承爸爸，自動「傳遞」獲得了劍和盾)\n",[20,5017,5019],{"id":5018},"錯誤的理解橫向的乾哥乾妹classc-classd","錯誤的理解：橫向的「乾哥乾妹」（ClassC ➔ ClassD）",[28,5021,5023],{"className":4987,"code":5022,"language":4989,"meta":33,"style":33},"ClassA (老祖宗)\n          ╱      ╲\n         ╱        ╲\n   ClassB (爸爸)  ClassD (叔叔)\n     ▼\n   ClassC (孫子)\n",[35,5024,5025,5030,5035,5040,5045,5050],{"__ignoreMap":33},[38,5026,5027],{"class":40,"line":41},[38,5028,5029],{},"ClassA (老祖宗)\n",[38,5031,5032],{"class":40,"line":47},[38,5033,5034],{},"          ╱      ╲\n",[38,5036,5037],{"class":40,"line":62},[38,5038,5039],{},"         ╱        ╲\n",[38,5041,5042],{"class":40,"line":173},[38,5043,5044],{},"   ClassB (爸爸)  ClassD (叔叔)\n",[38,5046,5047],{"class":40,"line":179},[38,5048,5049],{},"     ▼\n",[38,5051,5052],{"class":40,"line":185},[38,5053,5054],{},"   ClassC (孫子)\n",[85,5056,5058],{"id":5057},"等於javascript-的原型鏈prototype-chain","等於JavaScript 的原型鏈（Prototype Chain）",[20,5060,5062],{"id":5061},"_1-三層繼承代碼現場爺爸孫","1. 三層繼承代碼現場（爺、爸、孫）",[28,5064,5066],{"className":1794,"code":5065,"language":1796,"meta":33,"style":33},"\u002F\u002F 👴 爺爺級別（ClassA）：最基礎的動物\npublic class Animal\n{\n    public void Eat() => Console.WriteLine(\"吃東西囉！\");\n}\n\n\u002F\u002F 👨 爸爸級別（ClassB）：繼承爺爺，並擴充哺乳類特能\npublic class Mammal : Animal\n{\n    public void FeedMilk() => Console.WriteLine(\"餵母乳中...\");\n}\n\n\u002F\u002F 👶 孫子級別（ClassC）：繼承爸爸，並擴充貓咪特能\npublic class Cat : Mammal\n{\n    public void Meow() => Console.WriteLine(\"喵喵叫~\");\n}\n",[35,5067,5068,5073,5078,5082,5087,5091,5095,5100,5105,5109,5114,5118,5122,5127,5132,5136,5141],{"__ignoreMap":33},[38,5069,5070],{"class":40,"line":41},[38,5071,5072],{},"\u002F\u002F 👴 爺爺級別（ClassA）：最基礎的動物\n",[38,5074,5075],{"class":40,"line":47},[38,5076,5077],{},"public class Animal\n",[38,5079,5080],{"class":40,"line":62},[38,5081,165],{},[38,5083,5084],{"class":40,"line":173},[38,5085,5086],{},"    public void Eat() => Console.WriteLine(\"吃東西囉！\");\n",[38,5088,5089],{"class":40,"line":179},[38,5090,270],{},[38,5092,5093],{"class":40,"line":185},[38,5094,194],{"emptyLinePlaceholder":70},[38,5096,5097],{"class":40,"line":191},[38,5098,5099],{},"\u002F\u002F 👨 爸爸級別（ClassB）：繼承爺爺，並擴充哺乳類特能\n",[38,5101,5102],{"class":40,"line":197},[38,5103,5104],{},"public class Mammal : Animal\n",[38,5106,5107],{"class":40,"line":203},[38,5108,165],{},[38,5110,5111],{"class":40,"line":209},[38,5112,5113],{},"    public void FeedMilk() => Console.WriteLine(\"餵母乳中...\");\n",[38,5115,5116],{"class":40,"line":215},[38,5117,270],{},[38,5119,5120],{"class":40,"line":221},[38,5121,194],{"emptyLinePlaceholder":70},[38,5123,5124],{"class":40,"line":227},[38,5125,5126],{},"\u002F\u002F 👶 孫子級別（ClassC）：繼承爸爸，並擴充貓咪特能\n",[38,5128,5129],{"class":40,"line":232},[38,5130,5131],{},"public class Cat : Mammal\n",[38,5133,5134],{"class":40,"line":238},[38,5135,165],{},[38,5137,5138],{"class":40,"line":244},[38,5139,5140],{},"    public void Meow() => Console.WriteLine(\"喵喵叫~\");\n",[38,5142,5143],{"class":40,"line":249},[38,5144,270],{},[20,5146,5148],{"id":5147},"_2-孫子classc怎麼找到爺爺classa的","2. 孫子（ClassC）怎麼找到爺爺（ClassA）的？",[28,5150,5152],{"className":1794,"code":5151,"language":1796,"meta":33,"style":33},"{\n    static void Main()\n    {\n        \u002F\u002F 誕生一隻孫子級別的貓\n        Cat myCat = new Cat();\n\n        myCat.Meow();     \u002F\u002F 🟢 毫無疑問，這是 Cat（孫子）自己寫的超能力。\n        myCat.FeedMilk(); \u002F\u002F 🟢 找到了！這是從 Mammal（爸爸）那裡繼承來的。\n        myCat.Eat();      \u002F\u002F 🚀 找到了！這就是你問的：孫子成功的跨代找到了 Animal（爺爺）的 Eat()！\n    }\n}\n",[35,5153,5154,5158,5163,5167,5172,5177,5181,5186,5191,5196,5200],{"__ignoreMap":33},[38,5155,5156],{"class":40,"line":41},[38,5157,165],{},[38,5159,5160],{"class":40,"line":47},[38,5161,5162],{},"    static void Main()\n",[38,5164,5165],{"class":40,"line":62},[38,5166,176],{},[38,5168,5169],{"class":40,"line":173},[38,5170,5171],{},"        \u002F\u002F 誕生一隻孫子級別的貓\n",[38,5173,5174],{"class":40,"line":179},[38,5175,5176],{},"        Cat myCat = new Cat();\n",[38,5178,5179],{"class":40,"line":185},[38,5180,194],{"emptyLinePlaceholder":70},[38,5182,5183],{"class":40,"line":191},[38,5184,5185],{},"        myCat.Meow();     \u002F\u002F 🟢 毫無疑問，這是 Cat（孫子）自己寫的超能力。\n",[38,5187,5188],{"class":40,"line":197},[38,5189,5190],{},"        myCat.FeedMilk(); \u002F\u002F 🟢 找到了！這是從 Mammal（爸爸）那裡繼承來的。\n",[38,5192,5193],{"class":40,"line":203},[38,5194,5195],{},"        myCat.Eat();      \u002F\u002F 🚀 找到了！這就是你問的：孫子成功的跨代找到了 Animal（爺爺）的 Eat()！\n",[38,5197,5198],{"class":40,"line":209},[38,5199,264],{},[38,5201,5202],{"class":40,"line":215},[38,5203,270],{},[608,5205,5207],{"id":5206},"繼承是可轉移的transitive只要這條血脈不斷不論往上傳幾代最底層的孫子都能無縫享用老祖宗留下來的所有財產方法與欄位","繼承是可轉移的（Transitive）」。只要這條血脈不斷，不論往上傳幾代，最底層的孫子都能無縫享用老祖宗留下來的所有財產（方法與欄位）。",[11,5209,4182],{},[85,5211,5213],{"id":5212},"範例說明","範例說明:",[28,5215,5216],{"className":1794,"code":3005,"language":1796,"meta":33,"style":33},[35,5217,5218,5222,5226,5230,5234,5238,5242,5246,5250,5254,5258,5262,5266,5270,5274,5278,5282,5286,5290,5294,5298,5302,5306,5310,5314,5318,5322,5326,5330,5334,5338,5342,5346,5350,5354,5358,5362,5366,5370,5374,5378,5382,5386,5390,5394,5398,5402,5406,5410,5414,5418,5422,5426,5430,5434,5438,5442,5446,5450,5454,5458,5462,5466,5470,5474,5478,5482,5486,5490,5494,5498,5502,5506,5510,5514,5518,5522,5526,5530,5534,5538,5542,5546,5550,5554,5558],{"__ignoreMap":33},[38,5219,5220],{"class":40,"line":41},[38,5221,3012],{},[38,5223,5224],{"class":40,"line":47},[38,5225,3017],{},[38,5227,5228],{"class":40,"line":62},[38,5229,165],{},[38,5231,5232],{"class":40,"line":173},[38,5233,3026],{},[38,5235,5236],{"class":40,"line":179},[38,5237,3031],{},[38,5239,5240],{"class":40,"line":185},[38,5241,3036],{},[38,5243,5244],{"class":40,"line":191},[38,5245,194],{"emptyLinePlaceholder":70},[38,5247,5248],{"class":40,"line":197},[38,5249,3045],{},[38,5251,5252],{"class":40,"line":203},[38,5253,3050],{},[38,5255,5256],{"class":40,"line":209},[38,5257,3055],{},[38,5259,5260],{"class":40,"line":215},[38,5261,3060],{},[38,5263,5264],{"class":40,"line":221},[38,5265,3065],{},[38,5267,5268],{"class":40,"line":227},[38,5269,194],{"emptyLinePlaceholder":70},[38,5271,5272],{"class":40,"line":232},[38,5273,3074],{},[38,5275,5276],{"class":40,"line":238},[38,5277,3079],{},[38,5279,5280],{"class":40,"line":244},[38,5281,3084],{},[38,5283,5284],{"class":40,"line":249},[38,5285,3089],{},[38,5287,5288],{"class":40,"line":255},[38,5289,176],{},[38,5291,5292],{"class":40,"line":261},[38,5293,3098],{},[38,5295,5296],{"class":40,"line":267},[38,5297,3103],{},[38,5299,5300],{"class":40,"line":1264},[38,5301,3108],{},[38,5303,5304],{"class":40,"line":1269},[38,5305,3113],{},[38,5307,5308],{"class":40,"line":3116},[38,5309,264],{},[38,5311,5312],{"class":40,"line":3121},[38,5313,194],{"emptyLinePlaceholder":70},[38,5315,5316],{"class":40,"line":3126},[38,5317,3129],{},[38,5319,5320],{"class":40,"line":3132},[38,5321,3135],{},[38,5323,5324],{"class":40,"line":3138},[38,5325,176],{},[38,5327,5328],{"class":40,"line":3143},[38,5329,3146],{},[38,5331,5332],{"class":40,"line":3149},[38,5333,3152],{},[38,5335,5336],{"class":40,"line":3155},[38,5337,3158],{},[38,5339,5340],{"class":40,"line":3161},[38,5341,3164],{},[38,5343,5344],{"class":40,"line":3167},[38,5345,264],{},[38,5347,5348],{"class":40,"line":3172},[38,5349,194],{"emptyLinePlaceholder":70},[38,5351,5352],{"class":40,"line":3177},[38,5353,3180],{},[38,5355,5356],{"class":40,"line":3183},[38,5357,3186],{},[38,5359,5360],{"class":40,"line":3189},[38,5361,3192],{},[38,5363,5364],{"class":40,"line":3195},[38,5365,3198],{},[38,5367,5368],{"class":40,"line":3201},[38,5369,194],{"emptyLinePlaceholder":70},[38,5371,5372],{"class":40,"line":3206},[38,5373,3209],{},[38,5375,5376],{"class":40,"line":3212},[38,5377,3215],{},[38,5379,5380],{"class":40,"line":3218},[38,5381,3221],{},[38,5383,5384],{"class":40,"line":3224},[38,5385,194],{"emptyLinePlaceholder":70},[38,5387,5388],{"class":40,"line":3229},[38,5389,3232],{},[38,5391,5392],{"class":40,"line":3235},[38,5393,3238],{},[38,5395,5396],{"class":40,"line":3241},[38,5397,3244],{},[38,5399,5400],{"class":40,"line":3247},[38,5401,176],{},[38,5403,5404],{"class":40,"line":3252},[38,5405,3255],{},[38,5407,5408],{"class":40,"line":3258},[38,5409,3261],{},[38,5411,5412],{"class":40,"line":3264},[38,5413,264],{},[38,5415,5416],{"class":40,"line":3269},[38,5417,194],{"emptyLinePlaceholder":70},[38,5419,5420],{"class":40,"line":3274},[38,5421,3277],{},[38,5423,5424],{"class":40,"line":3280},[38,5425,3283],{},[38,5427,5428],{"class":40,"line":3286},[38,5429,3289],{},[38,5431,5432],{"class":40,"line":3292},[38,5433,3295],{},[38,5435,5436],{"class":40,"line":3298},[38,5437,270],{},[38,5439,5440],{"class":40,"line":3303},[38,5441,194],{"emptyLinePlaceholder":70},[38,5443,5444],{"class":40,"line":3308},[38,5445,3311],{},[38,5447,5448],{"class":40,"line":3314},[38,5449,3317],{},[38,5451,5452],{"class":40,"line":3320},[38,5453,3323],{},[38,5455,5456],{"class":40,"line":3326},[38,5457,165],{},[38,5459,5460],{"class":40,"line":3331},[38,5461,3334],{},[38,5463,5464],{"class":40,"line":3337},[38,5465,194],{"emptyLinePlaceholder":70},[38,5467,5468],{"class":40,"line":3342},[38,5469,3345],{},[38,5471,5472],{"class":40,"line":3348},[38,5473,3351],{},[38,5475,5476],{"class":40,"line":3354},[38,5477,3357],{},[38,5479,5480],{"class":40,"line":3360},[38,5481,3363],{},[38,5483,5484],{"class":40,"line":3366},[38,5485,194],{"emptyLinePlaceholder":70},[38,5487,5488],{"class":40,"line":3371},[38,5489,3374],{},[38,5491,5492],{"class":40,"line":3377},[38,5493,3380],{},[38,5495,5496],{"class":40,"line":3383},[38,5497,194],{"emptyLinePlaceholder":70},[38,5499,5500],{"class":40,"line":3388},[38,5501,3391],{},[38,5503,5504],{"class":40,"line":3394},[38,5505,3397],{},[38,5507,5508],{"class":40,"line":3400},[38,5509,3403],{},[38,5511,5512],{"class":40,"line":3406},[38,5513,176],{},[38,5515,5516],{"class":40,"line":3411},[38,5517,3414],{},[38,5519,5520],{"class":40,"line":3417},[38,5521,3420],{},[38,5523,5524],{"class":40,"line":3423},[38,5525,3426],{},[38,5527,5528],{"class":40,"line":3429},[38,5529,3255],{},[38,5531,5532],{"class":40,"line":3434},[38,5533,3437],{},[38,5535,5536],{"class":40,"line":3440},[38,5537,3443],{},[38,5539,5540],{"class":40,"line":3446},[38,5541,194],{"emptyLinePlaceholder":70},[38,5543,5544],{"class":40,"line":3451},[38,5545,3454],{},[38,5547,5548],{"class":40,"line":3457},[38,5549,3460],{},[38,5551,5552],{"class":40,"line":3463},[38,5553,3466],{},[38,5555,5556],{"class":40,"line":3469},[38,5557,264],{},[38,5559,5560],{"class":40,"line":3474},[38,5561,270],{},[28,5563,5565],{"className":1794,"code":5564,"language":1796,"meta":33,"style":33},"\u002F\u002F Create an instance of WorkItem by using the constructor in the\n\u002F\u002F base class that takes three arguments.\nWorkItem item = new(\"Fix Bugs\",\n            \"Fix all bugs in my code branch\",\n            new TimeSpan(3, 4, 0, 0));\n\n\u002F\u002F Create an instance of ChangeRequest by using the constructor in\n\u002F\u002F the derived class that takes four arguments.\nChangeRequest change = new(\"Change Base Class Design\",\n                  \"Add members to the class\",\n                  new TimeSpan(4, 0, 0),\n                  1);\n\n\u002F\u002F Use the ToString method defined in WorkItem.\nConsole.WriteLine(item.ToString());\n\n\u002F\u002F Use the inherited Update method to change the title of the\n\u002F\u002F ChangeRequest object.\nchange.Update(\"Change the Design of the Base Class\",\n    new TimeSpan(4, 0, 0));\n\n\u002F\u002F ChangeRequest inherits WorkItem's override of ToString.\nConsole.WriteLine(change.ToString());\n\u002F* Output:\n    1 - Fix Bugs\n    2 - Change the Design of the Base Class\n*\u002F\n",[35,5566,5567,5572,5577,5582,5587,5592,5596,5601,5606,5611,5616,5621,5626,5630,5635,5640,5644,5649,5654,5659,5664,5668,5673,5678,5683,5688,5693],{"__ignoreMap":33},[38,5568,5569],{"class":40,"line":41},[38,5570,5571],{},"\u002F\u002F Create an instance of WorkItem by using the constructor in the\n",[38,5573,5574],{"class":40,"line":47},[38,5575,5576],{},"\u002F\u002F base class that takes three arguments.\n",[38,5578,5579],{"class":40,"line":62},[38,5580,5581],{},"WorkItem item = new(\"Fix Bugs\",\n",[38,5583,5584],{"class":40,"line":173},[38,5585,5586],{},"            \"Fix all bugs in my code branch\",\n",[38,5588,5589],{"class":40,"line":179},[38,5590,5591],{},"            new TimeSpan(3, 4, 0, 0));\n",[38,5593,5594],{"class":40,"line":185},[38,5595,194],{"emptyLinePlaceholder":70},[38,5597,5598],{"class":40,"line":191},[38,5599,5600],{},"\u002F\u002F Create an instance of ChangeRequest by using the constructor in\n",[38,5602,5603],{"class":40,"line":197},[38,5604,5605],{},"\u002F\u002F the derived class that takes four arguments.\n",[38,5607,5608],{"class":40,"line":203},[38,5609,5610],{},"ChangeRequest change = new(\"Change Base Class Design\",\n",[38,5612,5613],{"class":40,"line":209},[38,5614,5615],{},"                  \"Add members to the class\",\n",[38,5617,5618],{"class":40,"line":215},[38,5619,5620],{},"                  new TimeSpan(4, 0, 0),\n",[38,5622,5623],{"class":40,"line":221},[38,5624,5625],{},"                  1);\n",[38,5627,5628],{"class":40,"line":227},[38,5629,194],{"emptyLinePlaceholder":70},[38,5631,5632],{"class":40,"line":232},[38,5633,5634],{},"\u002F\u002F Use the ToString method defined in WorkItem.\n",[38,5636,5637],{"class":40,"line":238},[38,5638,5639],{},"Console.WriteLine(item.ToString());\n",[38,5641,5642],{"class":40,"line":244},[38,5643,194],{"emptyLinePlaceholder":70},[38,5645,5646],{"class":40,"line":249},[38,5647,5648],{},"\u002F\u002F Use the inherited Update method to change the title of the\n",[38,5650,5651],{"class":40,"line":255},[38,5652,5653],{},"\u002F\u002F ChangeRequest object.\n",[38,5655,5656],{"class":40,"line":261},[38,5657,5658],{},"change.Update(\"Change the Design of the Base Class\",\n",[38,5660,5661],{"class":40,"line":267},[38,5662,5663],{},"    new TimeSpan(4, 0, 0));\n",[38,5665,5666],{"class":40,"line":1264},[38,5667,194],{"emptyLinePlaceholder":70},[38,5669,5670],{"class":40,"line":1269},[38,5671,5672],{},"\u002F\u002F ChangeRequest inherits WorkItem's override of ToString.\n",[38,5674,5675],{"class":40,"line":3116},[38,5676,5677],{},"Console.WriteLine(change.ToString());\n",[38,5679,5680],{"class":40,"line":3121},[38,5681,5682],{},"\u002F* Output:\n",[38,5684,5685],{"class":40,"line":3126},[38,5686,5687],{},"    1 - Fix Bugs\n",[38,5689,5690],{"class":40,"line":3132},[38,5691,5692],{},"    2 - Change the Design of the Base Class\n",[38,5694,5695],{"class":40,"line":3138},[38,5696,5697],{},"*\u002F\n",[20,5699,5701],{"id":5700},"_1-為什麼可以同時存在多個-workitem-的傢伙","1. 為什麼可以同時存在多個 WorkItem 的傢伙？",[28,5703,5705],{"className":1794,"code":5704,"language":1796,"meta":33,"style":33},"public WorkItem()\n    {\n        ID = 0;\n        Title = \"Default title\";\n        Description = \"Default description.\";\n        jobLength = new TimeSpan();\n    }\n\n    \u002F\u002F Instance constructor that has three parameters.\n    public WorkItem(string title, string desc, TimeSpan joblen)\n    {\n        ID = GetNextID();\n        Title = title;\n        Description = desc;\n        jobLength = joblen;\n    }\n\n    \u002F\u002F Static constructor to initialize the static member, currentID. This\n    \u002F\u002F constructor is called one time, automatically, before any instance\n    \u002F\u002F of WorkItem or ChangeRequest is created, or currentID is referenced.\n    static WorkItem() => currentID = 0;\n",[35,5706,5707,5712,5716,5720,5724,5728,5732,5736,5740,5744,5748,5752,5756,5760,5764,5768,5772,5776,5780,5784,5788],{"__ignoreMap":33},[38,5708,5709],{"class":40,"line":41},[38,5710,5711],{},"public WorkItem()\n",[38,5713,5714],{"class":40,"line":47},[38,5715,176],{},[38,5717,5718],{"class":40,"line":62},[38,5719,3098],{},[38,5721,5722],{"class":40,"line":173},[38,5723,3103],{},[38,5725,5726],{"class":40,"line":179},[38,5727,3108],{},[38,5729,5730],{"class":40,"line":185},[38,5731,3113],{},[38,5733,5734],{"class":40,"line":191},[38,5735,264],{},[38,5737,5738],{"class":40,"line":197},[38,5739,194],{"emptyLinePlaceholder":70},[38,5741,5742],{"class":40,"line":203},[38,5743,3129],{},[38,5745,5746],{"class":40,"line":209},[38,5747,3135],{},[38,5749,5750],{"class":40,"line":215},[38,5751,176],{},[38,5753,5754],{"class":40,"line":221},[38,5755,3146],{},[38,5757,5758],{"class":40,"line":227},[38,5759,3152],{},[38,5761,5762],{"class":40,"line":232},[38,5763,3158],{},[38,5765,5766],{"class":40,"line":238},[38,5767,3164],{},[38,5769,5770],{"class":40,"line":244},[38,5771,264],{},[38,5773,5774],{"class":40,"line":249},[38,5775,194],{"emptyLinePlaceholder":70},[38,5777,5778],{"class":40,"line":255},[38,5779,3180],{},[38,5781,5782],{"class":40,"line":261},[38,5783,3186],{},[38,5785,5786],{"class":40,"line":267},[38,5787,3192],{},[38,5789,5790],{"class":40,"line":1264},[38,5791,3198],{},[11,5793,5794],{},"========================",[20,5796,5798],{"id":5797},"_2多載overload","2.多載（Overload）",[11,5800,5801],{},"這兩個沒有寫 static，他們是為了 new 物件時服務的。",[28,5803,5805],{"className":1794,"code":5804,"language":1796,"meta":33,"style":33},"public WorkItem()\n    {\n        ID = 0;\n        Title = \"Default title\";\n        Description = \"Default description.\";\n        jobLength = new TimeSpan();\n    }\n\n    \u002F\u002F Instance constructor that has three parameters.\n    public WorkItem(string title, string desc, TimeSpan joblen)\n    {\n        ID = GetNextID();\n        Title = title;\n        Description = desc;\n        jobLength = joblen;\n    }\n\n",[35,5806,5807,5811,5815,5819,5823,5827,5831,5835,5839,5843,5847,5851,5855,5859,5863,5867],{"__ignoreMap":33},[38,5808,5809],{"class":40,"line":41},[38,5810,5711],{},[38,5812,5813],{"class":40,"line":47},[38,5814,176],{},[38,5816,5817],{"class":40,"line":62},[38,5818,3098],{},[38,5820,5821],{"class":40,"line":173},[38,5822,3103],{},[38,5824,5825],{"class":40,"line":179},[38,5826,3108],{},[38,5828,5829],{"class":40,"line":185},[38,5830,3113],{},[38,5832,5833],{"class":40,"line":191},[38,5834,264],{},[38,5836,5837],{"class":40,"line":197},[38,5838,194],{"emptyLinePlaceholder":70},[38,5840,5841],{"class":40,"line":203},[38,5842,3129],{},[38,5844,5845],{"class":40,"line":209},[38,5846,3135],{},[38,5848,5849],{"class":40,"line":215},[38,5850,176],{},[38,5852,5853],{"class":40,"line":221},[38,5854,3146],{},[38,5856,5857],{"class":40,"line":227},[38,5858,3152],{},[38,5860,5861],{"class":40,"line":232},[38,5862,3158],{},[38,5864,5865],{"class":40,"line":238},[38,5866,3164],{},[38,5868,5869],{"class":40,"line":244},[38,5870,264],{},[608,5872,5874],{"id":5873},"參數的數量或型別不同目的是為了讓別人在-new-workitem-時有不同的套餐可以選","參數的數量或型別不同。目的是為了讓別人在 new WorkItem() 時有不同的套餐可以選。",[11,5876,5794],{},[20,5878,5880],{"id":5879},"_3-靜態建構子第三個","3. 靜態建構子（第三個）",[28,5882,5884],{"className":1794,"code":5883,"language":1796,"meta":33,"style":33},"\u002F\u002F 3號：前面加上了 static！\nstatic WorkItem() => currentID = 0;\n",[35,5885,5886,5891],{"__ignoreMap":33},[38,5887,5888],{"class":40,"line":41},[38,5889,5890],{},"\u002F\u002F 3號：前面加上了 static！\n",[38,5892,5893],{"class":40,"line":47},[38,5894,5895],{},"static WorkItem() => currentID = 0;\n",[608,5897,5899],{"id":5898},"特色","特色:",[122,5901,5902,5905,5908,5911],{},[125,5903,5904],{},"無法主動呼叫它",[125,5906,5907],{},"只會執行一次",[125,5909,5910],{},"它是為了「太陽（靜態變數）」而生的：\n因為這棟大樓裡有一個 private static int currentID;（活在靜態儲存區的太陽）。微軟需要一個安全的地方，在太陽升起時把它初始化為 0。這就是這個靜態建構子的唯一任務。",[125,5912,5913],{},"嚴格的語法禁令：\n它絕對不能有任何參數（所以它無法被多載），也絕對不能寫 public 或 private。因為它是系統自動呼叫的，不需要對外公開權限。",[608,5915,5917],{"id":5916},"為什麼得這樣做","為什麼得這樣做?",[122,5919,5920,5923,5926],{},[125,5921,5922],{},"這四個限制根本不是為難工程師，它們完完全全就是一整套「硬核防呆保護機制」！ 而它存在的唯一方式，也正如你所說，是被動、而且只在 Class 第一次被驚醒時觸發的「開光儀式」。",[125,5924,5925],{},"不給它參數、不讓你寫 public\u002Fprivate、不讓你主動呼叫，就是為了把這個重大的初始化權限從人類手裡奪走，100% 交給系統代管。人類既然連碰都碰不到，就絕對沒有機會去搞砸這個全域變數。****",[125,5927,5928],{},"「被動建立 + 只執行一次」",[11,5930,5931],{},[3541,5932],{"alt":3543,"src":5933},"https:\u002F\u002Fhackmd.io\u002F_uploads\u002FBkQi_lOgzx.png",[608,5935,5936],{"id":5936},"參數也得分開",[28,5938,5940],{"className":1794,"code":5939,"language":1796,"meta":33,"style":33},"private static int currentID;\n",[35,5941,5942],{"__ignoreMap":33},[38,5943,5944],{"class":40,"line":41},[38,5945,5939],{},[122,5947,5948,5951,5954,5957],{},[125,5949,5950],{},"它是什麼： 靜態欄位（Static Field）。",[125,5952,5953],{},"它住哪裡： 靜態儲存區（Static Storage Area）。",[125,5955,5956],{},"物理現實： 只要看到 static，它就跟 new 蓋出來的實體大樓徹底分手。不管你的網頁現在有 0 個使用者、還是 10,000 個使用者，這個 currentID 在整個記憶體世界裡永遠只有唯一的一個格子。",[125,5958,5959],{},"白話比喻： 它是科學園區總部大門口的「總統計計數器」。",[28,5961,5963],{"className":1794,"code":5962,"language":1796,"meta":33,"style":33},"protected int ID { get; set; }\nprotected string Title { get; set; }\nprotected string Description { get; set; }\nprotected TimeSpan jobLength { get; set; }\n",[35,5964,5965,5970,5975,5980],{"__ignoreMap":33},[38,5966,5967],{"class":40,"line":41},[38,5968,5969],{},"protected int ID { get; set; }\n",[38,5971,5972],{"class":40,"line":47},[38,5973,5974],{},"protected string Title { get; set; }\n",[38,5976,5977],{"class":40,"line":62},[38,5978,5979],{},"protected string Description { get; set; }\n",[38,5981,5982],{"class":40,"line":173},[38,5983,5984],{},"protected TimeSpan jobLength { get; set; }\n",[122,5986,5987,5990,5993,5996],{},[125,5988,5989],{},"它們是什麼： 實例屬性（Instance Properties）。",[125,5991,5992],{},"它們住哪裡： Heap（堆積區）。",[125,5994,5995],{},"物理現實： 在你寫下 new WorkItem() 之前，這四個東西在記憶體裡根本不存在！只有當你每次呼叫 new，系統才會在 Heap 區像細胞分裂一樣，挖出一塊塊獨立的新空間，把這四個東西塞進去。",[125,5997,5998],{},"白話比喻： 它們是每間辦公室內部的「門牌、公司名稱、辦公室簡介、租約長度」。每開一家新公司（new 一個實例），就會誕生一組全新、獨立、互不干涉的屬性。",[11,6000,5794],{},[20,6002,6004],{"id":6003},"_4-時間順序","4. 時間順序",[608,6006,6008],{"id":6007},"地基動工靜態建構子優先開跑","地基動工（靜態建構子優先開跑）",[11,6010,6011],{},"當程式碼的指針走到 new ClassA()，海關（CLR）發現記憶體裡還沒有這種類別的蹤跡，它會當場按下暫停鍵。",[122,6013,6014,6017,6020],{},[125,6015,6016],{},"動作： 在你的 new 動作真正蓋出實體大樓之前，系統會搶先一步去執行 static ClassA()。",[125,6018,6019],{},"目的： 確保全域的太陽（靜態變數）已經安全地初始化完畢（例如把 currentID 設為 0）。",[125,6021,6022],{},"特點： 這個時候，實體物件根本還沒誕生，你傳進 new ClassA(\"參數\") 的那些參數，這時候通通被晾在一邊，完全不干它的事。",[608,6024,6026],{"id":6025},"房間裝潢依據參數挑選多載","房間裝潢（依據參數挑選多載）",[11,6028,6029],{},"當階段一的開光儀式圓滿結束後，系統才會放開暫停鍵，正式進入 new 的重頭戲。",[122,6031,6032,6035,6038],{},[125,6033,6034],{},"動作： CPU 在 Heap（堆積區）挖好一塊乾淨的記憶體，然後轉頭看你手裡拿著什麼門票。",[125,6036,6037],{},"如果你寫 new ClassA() ➔ 它就去敲「沒參數的預設款建構子」的門。",[125,6039,6040],{},"如果你寫 new ClassA(\"iPhone\", 30000) ➔ 它就去敲「帶有字串與數字的多載建構子」的門。",[11,6042,6043],{},"目的： 把你傳進來的參數，精準地塞進這棟剛蓋好的大樓房間裡（初始化實例屬性）。",[11,6045,5794],{},[608,6047,6049],{"id":6048},"_5-override","5. override",[28,6051,6053],{"className":1794,"code":6052,"language":1796,"meta":33,"style":33},"    public override string ToString() =>\n        $\"{this.ID} - {this.Title}\";\n",[35,6054,6055,6059],{"__ignoreMap":33},[38,6056,6057],{"class":40,"line":41},[38,6058,3289],{},[38,6060,6061],{"class":40,"line":47},[38,6062,3295],{},[11,6064,6065],{},"讓每一個你寫出來的 Class，都能擁有自己決定自己要長成什麼文字的超能力。以後不管是除錯（Debug）、印 Log、還是要在畫面上顯示，只要一呼叫它，大樓就會乖乖交出這張最精美、最看得懂的客製化名片！",[11,6067,5794],{},[608,6069,6071],{"id":6070},"_6-繼承的真相","6. 繼承的真相:",[122,6073,6074,6085],{},[125,6075,6076,6077],{},"物理真相:",[122,6078,6079,6082],{},[125,6080,6081],{},"繼承（Inheritance）帶給你的： 是「結構的繼承」。老爸保證你的骨架裡一定有 ID 和 Title。",[125,6083,6084],{},"建構子（Constructor）負責的： 是「數值的初始化」。大樓蓋好了，裡面有空房間（Title），但房間裡要擺沙發還是擺床（到底是 \"Default title\" 還是外部傳進來的 \"修正 Bug\"），必須由建構子在物件誕生那一刻決定。",[125,6086,6087,6088],{},"物理意義:",[122,6089,6090,6093],{},[125,6091,6092],{},"技能樹全自動複製（Code Reuse）：\n老爸身上只要是 public 或 protected 的方法（如 Update、GetNextID、ToString），自動百分之百焊死在兒子的技能樹上。兒子不需要重新發明輪子，直接拿來用就好。",[125,6094,6095],{},"型別認同（Is-A 關係）：\n在 C# 的法庭裡，「兒子就是老爸的一種特例（ChangeRequest IS A WorkItem）」。所有原本規定要傳入 WorkItem 的地方，你現在通通可以把 ChangeRequest 堂堂正正地丟進去。",[11,6097,5794],{},[608,6099,6101],{"id":6100},"_7-與前端差別","7. 與前端差別",[89,6103,6104],{},[11,6105,6106],{},"C# 的 : ＝ 前端的 extends。",[28,6108,6112],{"className":6109,"code":6110,"language":6111,"meta":33,"style":33},"language-typescript! shiki shiki-themes github-light github-dark","\u002F\u002F 前端寫法：用 extends\nclass ChangeRequest extends WorkItem {\n    \u002F\u002F ...\n}\n","typescript!",[35,6113,6114,6119,6124,6129],{"__ignoreMap":33},[38,6115,6116],{"class":40,"line":41},[38,6117,6118],{},"\u002F\u002F 前端寫法：用 extends\n",[38,6120,6121],{"class":40,"line":47},[38,6122,6123],{},"class ChangeRequest extends WorkItem {\n",[38,6125,6126],{"class":40,"line":62},[38,6127,6128],{},"    \u002F\u002F ...\n",[38,6130,6131],{"class":40,"line":173},[38,6132,270],{},[28,6134,6136],{"className":1794,"code":6135,"language":1796,"meta":33,"style":33},"\u002F\u002F C# 寫法：用 冒號 :\npublic class ChangeRequest : WorkItem\n{\n    ....\n}\n",[35,6137,6138,6143,6147,6151,6156],{"__ignoreMap":33},[38,6139,6140],{"class":40,"line":41},[38,6141,6142],{},"\u002F\u002F C# 寫法：用 冒號 :\n",[38,6144,6145],{"class":40,"line":47},[38,6146,3323],{},[38,6148,6149],{"class":40,"line":62},[38,6150,165],{},[38,6152,6153],{"class":40,"line":173},[38,6154,6155],{},"    ....\n",[38,6157,6158],{"class":40,"line":179},[38,6159,270],{},[11,6161,5794],{},[608,6163,6165],{"id":6164},"_8-base","8. Base:",[28,6167,6169],{"className":1794,"code":6168,"language":1796,"meta":33,"style":33},"public class ChangeRequest : WorkItem\n{\n    protected int originalItemID { get; set; }\n\n    \u002F\u002F 🚀 兒子收到了 4 個參數，它說：「前面 3 個是老爸的，直接用 : base 送去給老爸初始化！」\n    public ChangeRequest(string title, string desc, TimeSpan jobLen, int originalID) \n        : base(title, desc, jobLen) \u002F\u002F 👈 這行會直接觸發老爸的豪華建構子！\n    {\n        \u002F\u002F 兒子自己只需要專注處理自己發明的專利屬性就好！超級乾淨！\n        this.originalItemID = originalID;\n    }\n}\n",[35,6170,6171,6175,6179,6183,6187,6192,6197,6202,6206,6211,6215,6219],{"__ignoreMap":33},[38,6172,6173],{"class":40,"line":41},[38,6174,3323],{},[38,6176,6177],{"class":40,"line":47},[38,6178,165],{},[38,6180,6181],{"class":40,"line":62},[38,6182,3334],{},[38,6184,6185],{"class":40,"line":173},[38,6186,194],{"emptyLinePlaceholder":70},[38,6188,6189],{"class":40,"line":179},[38,6190,6191],{},"    \u002F\u002F 🚀 兒子收到了 4 個參數，它說：「前面 3 個是老爸的，直接用 : base 送去給老爸初始化！」\n",[38,6193,6194],{"class":40,"line":185},[38,6195,6196],{},"    public ChangeRequest(string title, string desc, TimeSpan jobLen, int originalID) \n",[38,6198,6199],{"class":40,"line":191},[38,6200,6201],{},"        : base(title, desc, jobLen) \u002F\u002F 👈 這行會直接觸發老爸的豪華建構子！\n",[38,6203,6204],{"class":40,"line":197},[38,6205,176],{},[38,6207,6208],{"class":40,"line":203},[38,6209,6210],{},"        \u002F\u002F 兒子自己只需要專注處理自己發明的專利屬性就好！超級乾淨！\n",[38,6212,6213],{"class":40,"line":209},[38,6214,3466],{},[38,6216,6217],{"class":40,"line":215},[38,6218,264],{},[38,6220,6221],{"class":40,"line":221},[38,6222,270],{},[11,6224,5794],{},[608,6226,6228],{"id":6227},"_9-其實可以原封不動","9. 其實可以原封不動",[122,6230,6231],{},[125,6232,6233],{},"可以把 WorkItem 的功能原封不動搬過來即可",[28,6235,6237],{"className":1794,"code":6236,"language":1796,"meta":33,"style":33},"\u002F\u002F 🏢 兒子對外宣告：我繼承老爸。然後加上大括號，裡面什麼都不寫！\npublic class ChangeRequest : WorkItem\n{\n    \u002F\u002F 這裡空空如也，連建構子、屬性、方法通通都不寫！\n}\n",[35,6238,6239,6244,6248,6252,6257],{"__ignoreMap":33},[38,6240,6241],{"class":40,"line":41},[38,6242,6243],{},"\u002F\u002F 🏢 兒子對外宣告：我繼承老爸。然後加上大括號，裡面什麼都不寫！\n",[38,6245,6246],{"class":40,"line":47},[38,6247,3323],{},[38,6249,6250],{"class":40,"line":62},[38,6251,165],{},[38,6253,6254],{"class":40,"line":173},[38,6255,6256],{},"    \u002F\u002F 這裡空空如也，連建構子、屬性、方法通通都不寫！\n",[38,6258,6259],{"class":40,"line":179},[38,6260,270],{},[11,6262,5794],{},[608,6264,3841],{"id":3840},[122,6266,6267,6270],{},[125,6268,6269],{},"靜態建構子是「類別本體（Class itself）」的點火開關，一定比任何物件實體（Instance）都還要早執行。",[125,6271,6272],{},"多載（Overload）是「實體物件」出生時的客製化套餐，等全域環境（靜態）準備好之後，才會根據你傳入的參數決定走哪一條多載路線。",[56,6274,58],{},{"title":33,"searchDepth":47,"depth":47,"links":6276},[6277,6281,6285],{"id":4979,"depth":47,"text":4980,"children":6278},[6279,6280],{"id":4983,"depth":62,"text":4984},{"id":5018,"depth":62,"text":5019},{"id":5057,"depth":47,"text":5058,"children":6282},[6283,6284],{"id":5061,"depth":62,"text":5062},{"id":5147,"depth":62,"text":5148},{"id":5212,"depth":47,"text":5213,"children":6286},[6287,6288,6289,6290],{"id":5700,"depth":62,"text":5701},{"id":5797,"depth":62,"text":5798},{"id":5879,"depth":62,"text":5880},{"id":6003,"depth":62,"text":6004},{},{"title":4958,"description":4965},"C#\u002F物件\u002F物件 02 繼承.md","notes\u002Fsync\u002Fcsharp-物件-物件-02-繼承",[76,4954],"xRKh8lypRxTOJDMQnVXeKHzXvSlcjqk_jjQCwaLlKpM",{"id":6298,"title":6299,"body":6300,"category":64,"date":4138,"description":6306,"draft":67,"extension":68,"meta":7941,"navigation":70,"path":4487,"seo":7942,"sourcePath":7943,"stem":7944,"tags":7945,"__hash__":7946},"notes\u002Fnotes\u002Fsync\u002Fcsharp-物件-物件-03-多型性.md","物件 03 多型性",{"type":8,"value":6301,"toc":7915},[6302,6307,6347,6350,6365,6373,6376,6380,6573,6575,6579,6590,6596,6605,6609,6662,6681,6683,6687,6691,6694,6698,6716,6718,6722,6733,6736,6757,6759,6763,6770,6829,6831,6839,6849,6857,6925,6927,6933,6941,6944,7014,7025,7102,7104,7108,7111,7115,7266,7270,7339,7343,7354,7381,7386,7389,7411,7419,7426,7450,7457,7549,7553,7559,7582,7602,7611,7613,7617,7624,7628,7665,7669,7678,7691,7695,7708,7740,7762,7764,7775,7784,7791,7850,7858,7913],[89,6303,6304],{},[11,6305,6306],{},"同一個方法名字，通電後可以表現出多種不同的狀態",[28,6308,6310],{"className":1649,"code":6309,"language":1651,"meta":33,"style":33},"                👑 多型 (Polymorphism)\n                           │\n         ┌─────────────────┴─────────────────┐\n         ▼                                   ▼\n【靜態多型 \u002F 編譯期多型】              【動態多型 \u002F 執行期多型】\n   ➔ 掌門人：Overload (多載)              ➔ 掌門人：Override (覆寫)\n   ➔ 物理戰場：編譯時看「參數」            ➔ 物理戰場：執行時看「Heap 肉身」\n",[35,6311,6312,6317,6322,6327,6332,6337,6342],{"__ignoreMap":33},[38,6313,6314],{"class":40,"line":41},[38,6315,6316],{},"                👑 多型 (Polymorphism)\n",[38,6318,6319],{"class":40,"line":47},[38,6320,6321],{},"                           │\n",[38,6323,6324],{"class":40,"line":62},[38,6325,6326],{},"         ┌─────────────────┴─────────────────┐\n",[38,6328,6329],{"class":40,"line":173},[38,6330,6331],{},"         ▼                                   ▼\n",[38,6333,6334],{"class":40,"line":179},[38,6335,6336],{},"【靜態多型 \u002F 編譯期多型】              【動態多型 \u002F 執行期多型】\n",[38,6338,6339],{"class":40,"line":185},[38,6340,6341],{},"   ➔ 掌門人：Overload (多載)              ➔ 掌門人：Override (覆寫)\n",[38,6343,6344],{"class":40,"line":191},[38,6345,6346],{},"   ➔ 物理戰場：編譯時看「參數」            ➔ 物理戰場：執行時看「Heap 肉身」\n",[85,6348,6349],{"id":6349},"物件的宣告型別與其執行階段型別將不再相同",[28,6351,6353],{"className":1794,"code":6352,"language":1796,"meta":33,"style":33},"\u002F\u002F ［左邊：宣告型別］ ➔ ［右邊：執行階段型別］\nWorkItem item = new ChangeRequest(\"改底層\", \"加成員\", time, 1);\n",[35,6354,6355,6360],{"__ignoreMap":33},[38,6356,6357],{"class":40,"line":41},[38,6358,6359],{},"\u002F\u002F ［左邊：宣告型別］ ➔ ［右邊：執行階段型別］\n",[38,6361,6362],{"class":40,"line":47},[38,6363,6364],{},"WorkItem item = new ChangeRequest(\"改底層\", \"加成員\", time, 1);\n",[122,6366,6367,6370],{},[125,6368,6369],{},"宣告型別（Declared Type）➔ 看左邊： 這是寫給「編譯器（Compiler）」看的。編譯器在檢查語法時，只認皮包的外殼。在它眼裡，item 變數的型別就是一個普通的 WorkItem。",[125,6371,6372],{},"執行階段型別（Runtime Type）➔ 看右邊： 這是程式開車後（Runtime），真正躺在 Heap 記憶體平民區 的物理肉身。此時這塊記憶體，完完全全是一個帶有頂樓加蓋（originalItemID）的 ChangeRequest 實體！",[11,6374,6375],{},"===========",[85,6377,6379],{"id":6378},"虛擬方法virtual","虛擬方法(virtual)",[28,6381,6383],{"className":1794,"code":6382,"language":1796,"meta":33,"style":33},"public class Shape\n{\n    \u002F\u002F A few example members\n    public int X { get; init; }\n    public int Y { get; init; }\n    public int Height { get; init; }\n    public int Width { get; init; }\n\n    \u002F\u002F Virtual method\n    public virtual void Draw()\n    {\n        Console.WriteLine(\"Performing base class drawing tasks\");\n    }\n}\n\npublic class Circle : Shape\n{\n    public override void Draw()\n    {\n        \u002F\u002F Code to draw a circle...\n        Console.WriteLine(\"Drawing a circle\");\n        base.Draw();\n    }\n}\npublic class Rectangle : Shape\n{\n    public override void Draw()\n    {\n        \u002F\u002F Code to draw a rectangle...\n        Console.WriteLine(\"Drawing a rectangle\");\n        base.Draw();\n    }\n}\npublic class Triangle : Shape\n{\n    public override void Draw()\n    {\n        \u002F\u002F Code to draw a triangle...\n        Console.WriteLine(\"Drawing a triangle\");\n        base.Draw();\n    }\n}\n",[35,6384,6385,6390,6394,6399,6404,6409,6414,6419,6423,6428,6433,6437,6442,6446,6450,6454,6459,6463,6468,6472,6477,6482,6487,6491,6495,6500,6504,6508,6512,6517,6522,6526,6530,6534,6539,6543,6547,6551,6556,6561,6565,6569],{"__ignoreMap":33},[38,6386,6387],{"class":40,"line":41},[38,6388,6389],{},"public class Shape\n",[38,6391,6392],{"class":40,"line":47},[38,6393,165],{},[38,6395,6396],{"class":40,"line":62},[38,6397,6398],{},"    \u002F\u002F A few example members\n",[38,6400,6401],{"class":40,"line":173},[38,6402,6403],{},"    public int X { get; init; }\n",[38,6405,6406],{"class":40,"line":179},[38,6407,6408],{},"    public int Y { get; init; }\n",[38,6410,6411],{"class":40,"line":185},[38,6412,6413],{},"    public int Height { get; init; }\n",[38,6415,6416],{"class":40,"line":191},[38,6417,6418],{},"    public int Width { get; init; }\n",[38,6420,6421],{"class":40,"line":197},[38,6422,194],{"emptyLinePlaceholder":70},[38,6424,6425],{"class":40,"line":203},[38,6426,6427],{},"    \u002F\u002F Virtual method\n",[38,6429,6430],{"class":40,"line":209},[38,6431,6432],{},"    public virtual void Draw()\n",[38,6434,6435],{"class":40,"line":215},[38,6436,176],{},[38,6438,6439],{"class":40,"line":221},[38,6440,6441],{},"        Console.WriteLine(\"Performing base class drawing tasks\");\n",[38,6443,6444],{"class":40,"line":227},[38,6445,264],{},[38,6447,6448],{"class":40,"line":232},[38,6449,270],{},[38,6451,6452],{"class":40,"line":238},[38,6453,194],{"emptyLinePlaceholder":70},[38,6455,6456],{"class":40,"line":244},[38,6457,6458],{},"public class Circle : Shape\n",[38,6460,6461],{"class":40,"line":249},[38,6462,165],{},[38,6464,6465],{"class":40,"line":255},[38,6466,6467],{},"    public override void Draw()\n",[38,6469,6470],{"class":40,"line":261},[38,6471,176],{},[38,6473,6474],{"class":40,"line":267},[38,6475,6476],{},"        \u002F\u002F Code to draw a circle...\n",[38,6478,6479],{"class":40,"line":1264},[38,6480,6481],{},"        Console.WriteLine(\"Drawing a circle\");\n",[38,6483,6484],{"class":40,"line":1269},[38,6485,6486],{},"        base.Draw();\n",[38,6488,6489],{"class":40,"line":3116},[38,6490,264],{},[38,6492,6493],{"class":40,"line":3121},[38,6494,270],{},[38,6496,6497],{"class":40,"line":3126},[38,6498,6499],{},"public class Rectangle : Shape\n",[38,6501,6502],{"class":40,"line":3132},[38,6503,165],{},[38,6505,6506],{"class":40,"line":3138},[38,6507,6467],{},[38,6509,6510],{"class":40,"line":3143},[38,6511,176],{},[38,6513,6514],{"class":40,"line":3149},[38,6515,6516],{},"        \u002F\u002F Code to draw a rectangle...\n",[38,6518,6519],{"class":40,"line":3155},[38,6520,6521],{},"        Console.WriteLine(\"Drawing a rectangle\");\n",[38,6523,6524],{"class":40,"line":3161},[38,6525,6486],{},[38,6527,6528],{"class":40,"line":3167},[38,6529,264],{},[38,6531,6532],{"class":40,"line":3172},[38,6533,270],{},[38,6535,6536],{"class":40,"line":3177},[38,6537,6538],{},"public class Triangle : Shape\n",[38,6540,6541],{"class":40,"line":3183},[38,6542,165],{},[38,6544,6545],{"class":40,"line":3189},[38,6546,6467],{},[38,6548,6549],{"class":40,"line":3195},[38,6550,176],{},[38,6552,6553],{"class":40,"line":3201},[38,6554,6555],{},"        \u002F\u002F Code to draw a triangle...\n",[38,6557,6558],{"class":40,"line":3206},[38,6559,6560],{},"        Console.WriteLine(\"Drawing a triangle\");\n",[38,6562,6563],{"class":40,"line":3212},[38,6564,6486],{},[38,6566,6567],{"class":40,"line":3218},[38,6568,264],{},[38,6570,6571],{"class":40,"line":3224},[38,6572,270],{},[272,6574],{},[20,6576,6578],{"id":6577},"一-實例化instantiation的物理真相工地開桌點菜","一、 實例化（Instantiation）的物理真相：工地開桌點菜",[11,6580,6581,6582,6585,6586,6589],{},"當你在程式碼裡寫下 ",[35,6583,6584],{},"public class Circle"," 等類別宣告時，在實體記憶體世界裡，並沒有任何一棟肉身大樓被蓋出來，它們通通只是躺在 ",[15,6587,6588],{},"【總店辦公室（Heap-Static）】"," 裡的紙質規格菜單。",[11,6591,6592,6593,6595],{},"直到程式通電，執行到 ",[35,6594,1556],{}," 的這一秒，才是藍圖演變成「物理肉身」的時刻：",[28,6597,6599],{"className":30,"code":6598,"language":32,"meta":33,"style":33},"Circle A = new Circle();\n",[35,6600,6601],{"__ignoreMap":33},[38,6602,6603],{"class":40,"line":41},[38,6604,6598],{},[608,6606,6608],{"id":6607},"️-點餐開桌的施工現場","🍽️ 點餐開桌的施工現場",[387,6610,6611,6621,6627,6644],{},[125,6612,6613,6616,6617,6620],{},[15,6614,6615],{},"抬頭看菜單（Heap-Static）","：CPU 探針衝進總店辦公室，翻開 ",[35,6618,6619],{},"Circle"," 的套餐規格。",[125,6622,6623,6626],{},[15,6624,6625],{},"計算托盤尺寸","：規格寫著：「這份套餐要裝下老爸的基礎房間（X, Y, Width, Height 共 16 節格子），還要加上 Circle 自己加碼的房間（Radius 4 節格子），外加物件頭外殼天線 16 節。」",[125,6628,6629,6632,6633,6636,6637,6640,6641,142],{},[15,6630,6631],{},"【工地現場灌漿（Heap-Heap）】","：CPU 轉身衝進 ",[15,6634,6635],{},"【工地現場餐桌區（Heap-Heap）】","，直接圈出一塊 ",[15,6638,6639],{},"36 節（Bytes）的連續地皮","。把老爸的房間和兒子的房間嚴絲合縫地焊在一起，蓋出",[15,6642,6643],{},"唯一的一棟「加肥版單一托盤大樓」",[125,6645,6646,6649,6650,6653,6654,6657,6658,6661],{},[15,6647,6648],{},"發放桌號收據（Stack）","：這桌蓋好後的實體地址門牌（假設是 ",[35,6651,6652],{},"0x5566","），被發射回 ",[15,6655,6656],{},"【工兵隨身工作檯（Stack）】","，塞進變數皮包 ",[35,6659,6660],{},"A"," 裡。",[89,6663,6664],{},[11,6665,6666,6667,6670,6671,6673,6674,6677,6678],{},"⚠️ ",[15,6668,6669],{},"鋼鐵校正：開兩桌，就是點裝兩次菜！","\n如果你在程式裡 ",[35,6672,1556],{}," 了 10 次，系統就會在 ",[15,6675,6676],{},"【Heap-Heap】"," 區開 10 張完全獨立、物理隔離的餐桌托盤（c1 ~ c10）。每一桌都有自己獨立影印、獨立加熱的一份合體食物資料。",[15,6679,6680],{},"開 10 個實例就是開 10 桌，絕對不會全部擠在同一桌！",[272,6682],{},[20,6684,6686],{"id":6685},"二-關於方法總店的料理步驟祕笈","二、 關於方法：總店的「料理步驟祕笈」",[608,6688,6690],{"id":6689},"️-本質是唯讀的烹飪指令流","⚙️ 本質是「唯讀的烹飪指令流」",[11,6692,6693],{},"方法（Method）的本質，就是一堆「冷酷的、唯讀的、只負責計算的機器指令（CPU Instructions）」。它不是負責在現場搬桌子、搞出實體的建構子工兵，編譯完後，它在底層就是純粹的**『烹飪動作流』**。",[608,6695,6697],{"id":6696},"方法要不要佔空間","💾 方法要不要佔空間？",[122,6699,6700,6710],{},[125,6701,6702,6705,6706,6709],{},[15,6703,6704],{},"要！但只佔在總店辦公室","：它們是唯讀的晶片指令，一出生就安穩地焊在 ",[15,6707,6708],{},"【總店中央廚房（Heap-Static）】"," 的祕笈檔案庫裡，全宇宙永遠只有一份。",[125,6711,6712,6715],{},[15,6713,6714],{},"與客人的餐桌完全隔離","：不論你外面開了 2 桌還是 10 桌，客人的餐桌托盤上都只裝自己的食物資料，絕對不會在餐桌上重複影印這些複雜的料理步驟。",[272,6717],{},[20,6719,6721],{"id":6720},"三-關於虛擬方法virtual點餐單上的自訂魔改插槽","三、 關於虛擬方法（Virtual）：點餐單上的「自訂魔改插槽」",[11,6723,6724,6725,6728,6729,6732],{},"既然都已經「繼承」了，兒子不就自動拿到老爸的方法了嗎？為什麼還要特別在老爸那裡加一個 ",[35,6726,6727],{},"virtual","（虛擬），在兒子那裡加一個 ",[35,6730,6731],{},"override","（覆寫）？",[608,6734,6735],{"id":4530},"🔬 物理真相",[122,6737,6738,6748],{},[125,6739,6740,6743,6744,6747],{},[15,6741,6742],{},"普通方法（沒有寫 virtual）管的是「代碼的重用（靜態死規矩）」","：\n老爸寫 ",[35,6745,6746],{},"public void PrintInfo()","，這就是鋼鐵死規矩（固定配菜）。兒子落地時直接自動沿用。全宇宙呼叫這個方法，CPU 都會盲目跑去中央廚房執行同一段老祕笈，吐出來的代碼都一模一樣。",[125,6749,6750,6753,6754,6756],{},[15,6751,6752],{},"虛擬方法（有寫 virtual）管的是「通電時認不認肉身（動態多型）」","：\n老爸在總店藍圖上挖了一個「彈性魔改插槽」，意思是：「這招我寫了個預設版步驟，外部分店兒子（子類別）開桌時可以隨時換成自己的獨門作法。」 兒子這才能在自己的單子裡寫 ",[35,6755,6731],{}," 去修改它。",[272,6758],{},[20,6760,6762],{"id":6761},"四-兒子覆寫override的物理分家","四、 兒子覆寫（Override）的物理分家",[11,6764,6765,6766,6769],{},"當鍵兒子決定加碼魔改，寫下 ",[35,6767,6768],{},"public override void Draw()"," 時，底層晶片會進行最漂亮的「步驟獨立、改接電話線」物理行為：",[387,6771,6772,6792],{},[125,6773,6774,6777,6778,6781,6782,407,6785],{},[15,6775,6776],{},"獨門步驟（機器碼）➔ 獨立開闢","：\n兒子的新方法指令，是獨立寫在 ",[15,6779,6780],{},"【Heap-Static】"," 中央廚房的新格子裡（例如 ",[35,6783,6784],{},"0x2222",[15,6786,6787,6788,6791],{},"老爸的原版步驟（",[35,6789,6790],{},"0x1111","）依然安穩地躺在原地，絕對沒有被複製，也沒有被消失！",[125,6793,6794,436,6797],{},[15,6795,6796],{},"點餐單表格（VMT \u002F 虛擬方法表）➔ 結構複製、指針改接",[122,6798,6799,6811],{},[125,6800,6801,6804,6805,6807,6808,142],{},[15,6802,6803],{},"結構複製","：繼承的那一秒，兒子在 ",[15,6806,6780],{}," 影印（複製）了一張老爸的點餐單格式（VMT 表格欄位），蓋了一張",[15,6809,6810],{},"屬於兒子自己私有、完全分家的新電話簿表格",[125,6812,6813,6816,6817,6819,6820,6828],{},[15,6814,6815],{},"指針改接","：因為寫了 ",[35,6818,6731],{},"，兒子拿起立可白，",[15,6821,6822,6823,6825,6826],{},"只在自己這張新單子上，把原本指向老爸廚房的 ",[35,6824,6790],{}," 地址，擦掉改接（更新）成指向自己新步驟的 ",[35,6827,6784],{},"。兩張表完全分家，互不干涉！",[272,6830],{},[20,6832,6834,6835,6838],{"id":6833},"五-揭秘-base-關鍵字多型宇宙的超高速傳送門","五、 揭秘 ",[35,6836,6837],{},"base"," 關鍵字：多型宇宙的超高速傳送門",[11,6840,6841,6842,6845,6846,6848],{},"當兒子在自己的新代碼裡寫了 ",[35,6843,6844],{},"base.Draw();","，這個 ",[35,6847,6837],{}," 在底層根本不是一個新物件，而是一個**「編譯期魔術貼紙（VMT 繞道指令）」**。",[608,6850,6852,6853,6856],{"id":6851},"basedraw-通電時的硬體超能力","🚀 ",[35,6854,6855],{},"base.Draw()"," 通電時的硬體超能力",[122,6858,6859,6881],{},[125,6860,6861,6868,6869,6872,6873],{},[15,6862,6863,6864,6867],{},"呼叫 ",[35,6865,6866],{},"this.Draw()","（普通虛擬呼叫）"," ➔ 編譯成 ",[35,6870,6871],{},"callvirt"," 指令。\n",[122,6874,6875],{},[125,6876,6877,6880],{},[15,6878,6879],{},"CPU 動作","：去翻 Heap-Heap 的大樓頭頂貼紙，尋線去查 Heap-Static 兒子的 VMT 表，查到誰就執行誰。若兒子呼叫自己，會陷入無限遞迴導致 Stack 暴斃。",[125,6882,6883,6868,6888,6891,6892,6895,6896],{},[15,6884,6863,6885,6887],{},[35,6886,6855],{},"（老爸強行呼叫）",[35,6889,6890],{},"call"," 指令（",[15,6893,6894],{},"靜態直接彈射","）。\n",[122,6897,6898,6913],{},[125,6899,6900,6903,6904,6906,6907,6909,6910,142],{},[15,6901,6902],{},"編譯期硬焊","：編譯器在編譯期翻開老爸那張沒被動過的原始單子，查到地址是 ",[35,6905,6790],{},"。編譯器直接在兒子的指令裡**硬焊（Hardcode）**打上老爸機房的物理 GPS 座標，把 ",[35,6908,6855],{}," 換成 ",[35,6911,6912],{},"call 0x1111",[125,6914,6915,6918,6919,6921,6922],{},[15,6916,6917],{},"執行期繞道","：通電執行時，",[35,6920,6837],{}," 在物理上直接蒸發（佔用 0 位元組）。CPU ",[15,6923,6924],{},"完全無視、繞過",[272,6926],{},[20,6928,6930,6931],{"id":6929},"六-幕後黑魔法世界上根本沒有實體方法與隱藏的-this","六、 幕後黑魔法：世界上根本沒有「實體方法」與隱藏的 ",[35,6932,96],{},[11,6934,2855,6935,6937,6938,6940],{},[15,6936,6780],{}," 中央廚房區裡，不論是老爸的方法還是兒子的方法，在編譯成機器碼後，肚子裡其實都偷偷藏了一個全天下工程師都看不到的**「0號隱藏參數」，叫做 ",[35,6939,96],{}," 指針**！",[11,6942,6943],{},"在 CPU 眼裡，這兩個方法底層真正的物理長相其實是這樣的：",[28,6945,6947],{"className":30,"code":6946,"language":32,"meta":33,"style":33},"\u002F\u002F 老爸的方法，在底層其實長這樣：\npublic static void Shape_Draw(Shape __this) \u002F\u002F 👈 偷偷收一個肉身地址！\n{\n    \u002F\u002F 這裡如果存取 __this.X，就是去讀那個地址的資料\n    Console.WriteLine(\"Performing base class drawing tasks\");\n}\n\n\u002F\u002F 兒子覆寫的方法，在底層其實長這樣：\npublic static void Circle_Draw(Circle __this) \u002F\u002F 👈 同樣偷偷收一個肉身地址！\n{\n    Console.WriteLine(\"Drawing a circle\");\n    \u002F\u002F base.Draw(); 在底層被編譯器翻譯成：\n    Shape_Draw(__this); \u002F\u002F 🚀 物理真相就在這一行！直接把自己的肉身傳過去\n}\n",[35,6948,6949,6954,6959,6963,6968,6973,6977,6981,6986,6991,6995,7000,7005,7010],{"__ignoreMap":33},[38,6950,6951],{"class":40,"line":41},[38,6952,6953],{},"\u002F\u002F 老爸的方法，在底層其實長這樣：\n",[38,6955,6956],{"class":40,"line":47},[38,6957,6958],{},"public static void Shape_Draw(Shape __this) \u002F\u002F 👈 偷偷收一個肉身地址！\n",[38,6960,6961],{"class":40,"line":62},[38,6962,165],{},[38,6964,6965],{"class":40,"line":173},[38,6966,6967],{},"    \u002F\u002F 這裡如果存取 __this.X，就是去讀那個地址的資料\n",[38,6969,6970],{"class":40,"line":179},[38,6971,6972],{},"    Console.WriteLine(\"Performing base class drawing tasks\");\n",[38,6974,6975],{"class":40,"line":185},[38,6976,270],{},[38,6978,6979],{"class":40,"line":191},[38,6980,194],{"emptyLinePlaceholder":70},[38,6982,6983],{"class":40,"line":197},[38,6984,6985],{},"\u002F\u002F 兒子覆寫的方法，在底層其實長這樣：\n",[38,6987,6988],{"class":40,"line":203},[38,6989,6990],{},"public static void Circle_Draw(Circle __this) \u002F\u002F 👈 同樣偷偷收一個肉身地址！\n",[38,6992,6993],{"class":40,"line":209},[38,6994,165],{},[38,6996,6997],{"class":40,"line":215},[38,6998,6999],{},"    Console.WriteLine(\"Drawing a circle\");\n",[38,7001,7002],{"class":40,"line":221},[38,7003,7004],{},"    \u002F\u002F base.Draw(); 在底層被編譯器翻譯成：\n",[38,7006,7007],{"class":40,"line":227},[38,7008,7009],{},"    Shape_Draw(__this); \u002F\u002F 🚀 物理真相就在這一行！直接把自己的肉身傳過去\n",[38,7011,7012],{"class":40,"line":232},[38,7013,270],{},[608,7015,7017,7018,7020,7021,7024],{"id":7016},"還原-basedraw-通電時的物理肉身流向-circle-a-new-circle-adraw","🎬 還原 ",[35,7019,6855],{}," 通電時的物理肉身流向 (",[35,7022,7023],{},"Circle A = new Circle(); A.Draw();",")",[387,7026,7027,7067,7083],{},[125,7028,7029,7032,7033,7035,7036,7038,7039,7041,7042,7045,7046],{},[15,7030,7031],{},"第一幕：外部通電，衝進兒子方法","\nCPU 帶著 ",[35,7034,6660],{}," 大樓在 ",[15,7037,6676],{}," 的實體地址（假設是 ",[35,7040,6652],{},"），衝進兒子的 ",[35,7043,7044],{},"Circle_Draw"," 晶片。\n",[122,7047,7048,7061],{},[125,7049,7050,7051,7054,7055,7057,7058,407],{},"此時方法內部的 ",[35,7052,7053],{},"__this"," ＝ ",[35,7056,6652],{},"（",[15,7059,7060],{},"主體是 Circle 肉身",[125,7062,7063,7064,142],{},"螢幕印出：",[35,7065,7066],{},"\"Drawing a circle\"",[125,7068,7069,7072,7073,7076,7077],{},[15,7070,7071],{},"第二幕：撞到 base.Draw() ➔ 執行 Shape_Draw(__this);","\n兒子的晶片執行到最後一行，它呼叫老爸的 ",[35,7074,7075],{},"Shape_Draw"," 晶片。關鍵來了：",[15,7078,7079,7080,7082],{},"兒子把自己手裡的 ",[35,7081,6652],{}," 地址（自己的肉身），原封不動地當作參數，傳給了老爸的方法！",[125,7084,7085,7088,7089,7091,7092,7094,7095,7097,7098,7101],{},[15,7086,7087],{},"第三幕：老爸代碼通電，但用的是兒子的肉身！","\nCPU 探針彈射到老爸的 ",[35,7090,7075],{}," 機器碼區。老爸的晶片啟動了，此時老爸方法肚子裡的 ",[35,7093,7053],{}," 拿到了 ",[35,7096,6652],{},"！",[15,7099,7100],{},"依然是那棟 Circle 大樓！"," 老爸的指令順利借用兒子大樓裡面的資料欄位完成運算。",[272,7103],{},[20,7105,7107],{"id":7106},"七-商業實戰落地購物車與折價券引擎多型權重與雙肉身指標流向","七、 商業實戰落地：購物車與折價券引擎（多型、權重與雙肉身指標流向）",[11,7109,7110],{},"在真實商業世界中，我們絕對不會把物件死死寫在 List 裡，而是動態從資料庫抓取。以下是多型控管「計算順序（先扣錢還是先打折）」的終極商業架構。",[608,7112,7114],{"id":7113},"_1-購物車與折價券菜單規格heap-static","📊 1. 購物車與折價券菜單規格（Heap-Static）",[28,7116,7118],{"className":30,"code":7117,"language":32,"meta":33,"style":33},"\u002F\u002F 🛒 購物車資料肉身規格\npublic class Cart\n{\n    public int Id { get; set; }\n    public decimal TotalAmount { get; set; } \u002F\u002F 💰 隨時被修改的總金額格子\n}\n\n\u002F\u002F 🎖️ 總店的基礎折價券菜單（老爸）\npublic abstract class CouponRule\n{\n    public string Code { get; set; }\n    public int Priority { get; set; } \u002F\u002F 📌 魔鬼在這裡：數字越小越先執行！\n    public abstract void ApplyDiscount(Cart cart); \u002F\u002F 魔改插槽\n}\n\n\u002F\u002F 2. 衍生菜單 A（滿千折百兒子）\npublic class AmountCoupon : CouponRule\n{\n    public decimal Threshold { get; set; } \u002F\u002F 門檻\n    public decimal DiscountAmount { get; set; } \u002F\u002F 折多少\n    public override void ApplyDiscount(Cart cart)\n    {\n        if (cart.TotalAmount >= Threshold) { cart.TotalAmount -= DiscountAmount; }\n    }\n}\n\n\u002F\u002F 3. 衍生菜單 B（打折兒子）\npublic class PercentageCoupon : CouponRule\n{\n    public decimal Percentage { get; set; } \u002F\u002F 打幾折\n    public override void ApplyDiscount(Cart cart) { cart.TotalAmount *= Percentage; }\n}\n",[35,7119,7120,7125,7130,7134,7139,7144,7148,7152,7157,7162,7166,7170,7175,7180,7184,7188,7193,7198,7202,7207,7212,7217,7221,7226,7230,7234,7238,7243,7248,7252,7257,7262],{"__ignoreMap":33},[38,7121,7122],{"class":40,"line":41},[38,7123,7124],{},"\u002F\u002F 🛒 購物車資料肉身規格\n",[38,7126,7127],{"class":40,"line":47},[38,7128,7129],{},"public class Cart\n",[38,7131,7132],{"class":40,"line":62},[38,7133,165],{},[38,7135,7136],{"class":40,"line":173},[38,7137,7138],{},"    public int Id { get; set; }\n",[38,7140,7141],{"class":40,"line":179},[38,7142,7143],{},"    public decimal TotalAmount { get; set; } \u002F\u002F 💰 隨時被修改的總金額格子\n",[38,7145,7146],{"class":40,"line":185},[38,7147,270],{},[38,7149,7150],{"class":40,"line":191},[38,7151,194],{"emptyLinePlaceholder":70},[38,7153,7154],{"class":40,"line":197},[38,7155,7156],{},"\u002F\u002F 🎖️ 總店的基礎折價券菜單（老爸）\n",[38,7158,7159],{"class":40,"line":203},[38,7160,7161],{},"public abstract class CouponRule\n",[38,7163,7164],{"class":40,"line":209},[38,7165,165],{},[38,7167,7168],{"class":40,"line":215},[38,7169,4282],{},[38,7171,7172],{"class":40,"line":221},[38,7173,7174],{},"    public int Priority { get; set; } \u002F\u002F 📌 魔鬼在這裡：數字越小越先執行！\n",[38,7176,7177],{"class":40,"line":227},[38,7178,7179],{},"    public abstract void ApplyDiscount(Cart cart); \u002F\u002F 魔改插槽\n",[38,7181,7182],{"class":40,"line":232},[38,7183,270],{},[38,7185,7186],{"class":40,"line":238},[38,7187,194],{"emptyLinePlaceholder":70},[38,7189,7190],{"class":40,"line":244},[38,7191,7192],{},"\u002F\u002F 2. 衍生菜單 A（滿千折百兒子）\n",[38,7194,7195],{"class":40,"line":249},[38,7196,7197],{},"public class AmountCoupon : CouponRule\n",[38,7199,7200],{"class":40,"line":255},[38,7201,165],{},[38,7203,7204],{"class":40,"line":261},[38,7205,7206],{},"    public decimal Threshold { get; set; } \u002F\u002F 門檻\n",[38,7208,7209],{"class":40,"line":267},[38,7210,7211],{},"    public decimal DiscountAmount { get; set; } \u002F\u002F 折多少\n",[38,7213,7214],{"class":40,"line":1264},[38,7215,7216],{},"    public override void ApplyDiscount(Cart cart)\n",[38,7218,7219],{"class":40,"line":1269},[38,7220,176],{},[38,7222,7223],{"class":40,"line":3116},[38,7224,7225],{},"        if (cart.TotalAmount >= Threshold) { cart.TotalAmount -= DiscountAmount; }\n",[38,7227,7228],{"class":40,"line":3121},[38,7229,264],{},[38,7231,7232],{"class":40,"line":3126},[38,7233,270],{},[38,7235,7236],{"class":40,"line":3132},[38,7237,194],{"emptyLinePlaceholder":70},[38,7239,7240],{"class":40,"line":3138},[38,7241,7242],{},"\u002F\u002F 3. 衍生菜單 B（打折兒子）\n",[38,7244,7245],{"class":40,"line":3143},[38,7246,7247],{},"public class PercentageCoupon : CouponRule\n",[38,7249,7250],{"class":40,"line":3149},[38,7251,165],{},[38,7253,7254],{"class":40,"line":3155},[38,7255,7256],{},"    public decimal Percentage { get; set; } \u002F\u002F 打幾折\n",[38,7258,7259],{"class":40,"line":3161},[38,7260,7261],{},"    public override void ApplyDiscount(Cart cart) { cart.TotalAmount *= Percentage; }\n",[38,7263,7264],{"class":40,"line":3167},[38,7265,270],{},[608,7267,7269],{"id":7268},"️-2-萬用結帳核心引擎web-api-controller-控場","🏎️ 2. 萬用結帳核心引擎（Web API Controller 控場）",[28,7271,7273],{"className":30,"code":7272,"language":32,"meta":33,"style":33},"public void Checkout(string userId, Cart cart)\n{\n    \u002F\u002F 🚀 關鍵 1：從資料庫動態撈出使用者勾選的所有折價券大樓指標\n    List\u003CCouponRule> userCoupons = _db.GetAppliedCoupons(userId);\n\n    \u002F\u002F 🎖️ 關鍵 2（順序真相）：管你是什麼券，通通按照 Priority 數字從小到大排好隊！\n    var sortedCoupons = userCoupons.OrderBy(c => c.Priority).ToList();\n\n    \u002F\u002F 毫無感情的萬用引擎依序發射 callvirt 指令：\n    foreach (var coupon in sortedCoupons)\n    {\n        coupon.ApplyDiscount(cart); \u002F\u002F 🎯 多型的萬用雷射就在這一行！\n    }\n}\n",[35,7274,7275,7280,7284,7289,7294,7298,7303,7308,7312,7317,7322,7326,7331,7335],{"__ignoreMap":33},[38,7276,7277],{"class":40,"line":41},[38,7278,7279],{},"public void Checkout(string userId, Cart cart)\n",[38,7281,7282],{"class":40,"line":47},[38,7283,165],{},[38,7285,7286],{"class":40,"line":62},[38,7287,7288],{},"    \u002F\u002F 🚀 關鍵 1：從資料庫動態撈出使用者勾選的所有折價券大樓指標\n",[38,7290,7291],{"class":40,"line":173},[38,7292,7293],{},"    List\u003CCouponRule> userCoupons = _db.GetAppliedCoupons(userId);\n",[38,7295,7296],{"class":40,"line":179},[38,7297,194],{"emptyLinePlaceholder":70},[38,7299,7300],{"class":40,"line":185},[38,7301,7302],{},"    \u002F\u002F 🎖️ 關鍵 2（順序真相）：管你是什麼券，通通按照 Priority 數字從小到大排好隊！\n",[38,7304,7305],{"class":40,"line":191},[38,7306,7307],{},"    var sortedCoupons = userCoupons.OrderBy(c => c.Priority).ToList();\n",[38,7309,7310],{"class":40,"line":197},[38,7311,194],{"emptyLinePlaceholder":70},[38,7313,7314],{"class":40,"line":203},[38,7315,7316],{},"    \u002F\u002F 毫無感情的萬用引擎依序發射 callvirt 指令：\n",[38,7318,7319],{"class":40,"line":209},[38,7320,7321],{},"    foreach (var coupon in sortedCoupons)\n",[38,7323,7324],{"class":40,"line":215},[38,7325,176],{},[38,7327,7328],{"class":40,"line":221},[38,7329,7330],{},"        coupon.ApplyDiscount(cart); \u002F\u002F 🎯 多型的萬用雷射就在這一行！\n",[38,7332,7333],{"class":40,"line":227},[38,7334,264],{},[38,7336,7337],{"class":40,"line":232},[38,7338,270],{},[608,7340,7342],{"id":7341},"_3-數據流實況模擬把隱形的順序看個清清楚楚","📈 3. 數據流實況模擬：把隱形的「順序」看個清清楚楚！",[11,7344,7345,7346,7349,7350,7353],{},"為了看穿順序，我們模擬一次真實的動態執行。\n假設現在",[15,7347,7348],{},"購物車原始金額為 1100 元"," (",[35,7351,7352],{},"cart.TotalAmount = 1100",")，使用者在前端勾選了兩張券，背後資料庫設定的權重如下：",[122,7355,7356,7369],{},[125,7357,7358,7349,7361,7364,7365,7368],{},[15,7359,7360],{},"滿千折百券",[35,7362,7363],{},"AmountCoupon",")：行銷設定 ",[35,7366,7367],{},"Priority = 10"," (優先權高)",[125,7370,7371,7349,7374,7364,7377,7380],{},[15,7372,7373],{},"全局九折券",[35,7375,7376],{},"PercentageCoupon",[35,7378,7379],{},"Priority = 20"," (優先權低)",[7382,7383,7385],"h5",{"id":7384},"第一步資料庫撈出來時usercoupons-狀態","📍 第一步：資料庫撈出來時（userCoupons 狀態）",[11,7387,7388],{},"這時從資料庫撈出來的 List 順序可能是亂的（例如打折券排在前面）：",[122,7390,7391,7400],{},[125,7392,7393,7396,7397,7399],{},[35,7394,7395],{},"userCoupons[0]"," ➔ ",[35,7398,7376],{}," (Priority = 20)",[125,7401,7402,7396,7405,7407,7408],{},[35,7403,7404],{},"userCoupons[1]",[35,7406,7363],{}," (Priority = 10)\n",[1347,7409,7410],{},"(如果直接跑這團亂序，金額會算錯！)",[7382,7412,7414,7415,7418],{"id":7413},"第二步執行完-orderbyc-cpriority-後sortedcoupons-狀態","📍 第二步：執行完 ",[35,7416,7417],{},"OrderBy(c => c.Priority)"," 後（sortedCoupons 狀態）",[11,7420,7421,7422,7425],{},"經過這一行排序後，List 內部的指標順序",[15,7423,7424],{},"在物理上被重新排隊","了！變成：",[122,7427,7428,7439],{},[125,7429,7430,7431,7396,7434,7349,7436,7024],{},"1️⃣ ",[35,7432,7433],{},"sortedCoupons[0]",[35,7435,7363],{},[15,7437,7438],{},"Priority = 10，成功排到老大！",[125,7440,7441,7442,7396,7445,7349,7447,7024],{},"2️⃣ ",[35,7443,7444],{},"sortedCoupons[1]",[35,7446,7376],{},[15,7448,7449],{},"Priority = 20，乖乖排到老二！",[7382,7451,7453,7454,7456],{"id":7452},"第三步進入-foreach-晶片狂飆","📍 第三步：進入 ",[35,7455,308],{}," 晶片狂飆",[122,7458,7459,7507],{},[125,7460,7461,7464],{},[15,7462,7463],{},"🔄 第一次迴圈（coupon ＝ 滿千折百老大）：",[122,7465,7466,7475,7484,7496],{},[125,7467,7468,336,7471,7474],{},[15,7469,7470],{},"當前購物車金額",[35,7472,7473],{},"1100"," 元。",[125,7476,7477,7480,7481,7483],{},[15,7478,7479],{},"動態認親","：CPU 摸到老大天線，彈射去跑 ",[35,7482,7363],{}," 的扣錢晶片。",[125,7485,7486,336,7489,7492,7493,142],{},[15,7487,7488],{},"執行數學",[35,7490,7491],{},"1100 >= 1000"," 成立！➔ ",[35,7494,7495],{},"1100 - 100 = 1000",[125,7497,7498,336,7501,142],{},[15,7499,7500],{},"此時購物車格子被物理改寫為",[15,7502,7503,7506],{},[35,7504,7505],{},"1000"," 元",[125,7508,7509,7512],{},[15,7510,7511],{},"🔄 第二次迴圈（coupon ＝ 九折老二）：",[122,7513,7514,7523,7531,7538],{},[125,7515,7516,7518,7519,7097],{},[15,7517,7470],{},"：已經變成上一步留下來的 ",[15,7520,7521,7506],{},[35,7522,7505],{},[125,7524,7525,7527,7528,7530],{},[15,7526,7479],{},"：CPU 摸到老二天線，彈射去跑 ",[35,7529,7376],{}," 的打折晶片。",[125,7532,7533,336,7535,142],{},[15,7534,7488],{},[35,7536,7537],{},"1000 * 0.9 = 900",[125,7539,7540,336,7543,7548],{},[15,7541,7542],{},"最終結帳金額",[15,7544,7545,7506],{},[35,7546,7547],{},"900","（成功先扣完錢、再打折！）。",[608,7550,7552],{"id":7551},"_4-核心引擎通電時的雙肉身指標流向","🔍 4. 核心引擎通電時的「雙肉身指標流向」",[11,7554,7555,7556],{},"當迴圈在跑時，底層晶片電路實際上是被翻譯成直接傳遞記憶體地址：\n",[35,7557,7558],{},"AmountCoupon_ApplyDiscount(coupon_address, cart_address);",[122,7560,7561,7571],{},[125,7562,7563,7568,7569,407],{},[15,7564,7565,7566,367],{},"指標一（隱藏的 ",[35,7567,7053],{},"：拿著當下那張折價券大樓的 Heap 地址（",[35,7570,6784],{},[125,7572,7573,7579,7580,407],{},[15,7574,7575,7576,367],{},"指標二（一般的 ",[35,7577,7578],{},"cart","：拿著購物車大樓的 Heap 地址（",[35,7581,6790],{},[11,7583,7584,7587,7588,7590,7591,7594,7595,7597,7598,7601],{},[15,7585,7586],{},"兩股電流同時導通","：CPU 站在中央廚房的唯讀晶片上，左手拿著 ",[35,7589,6784],{}," 地址去讀取這張折價券專屬的自訂欄位（讀到權重 ",[35,7592,7593],{},"Priority"," 或是折價金額）；右手拿著 ",[35,7596,6790],{}," 地址，直接橫跨地皮，物理改寫了購物車大樓肚子裡的 ",[35,7599,7600],{},"TotalAmount"," 格子！",[11,7603,7604,7605,7607,7608,7610],{},"這就是為什麼方法不需要在實體世界多開區域存放資料——因為方法本身是乾癟的唯讀電路，它在執行時，同時吸取了「折價券肉身（",[35,7606,96],{},"）」與「購物車肉身（",[35,7609,7578],{},"）」的微觀資料，才完美完成了這場商業運算！",[272,7612],{},[85,7614,7616],{"id":7615},"八-官方文件解密為什麼欄位不可為虛擬死資料-vs-可變動邏輯","八、 官方文件解密：為什麼「欄位不可為虛擬」？（死資料 vs 可變動邏輯）",[11,7618,7619,7620,7623],{},"微軟官方文件提到：「",[15,7621,7622],{},"欄位不可為虛擬（Fields cannot be virtual）","，只有方法、屬性、事件和索引子可以是虛擬的。」這在底層是鋼鐵般的硬體物理限制。",[20,7625,7627],{"id":7626},"_1-c-術語大澄清成員members與欄位fields","🧬 1. C# 術語大澄清：「成員（Members）」與「欄位（Fields）」",[122,7629,7630,7636],{},[125,7631,7632,7635],{},[15,7633,7634],{},"成員（Members）","：大集合統稱。只要寫在類別（Class）肚子裡的東西（欄位、方法、屬性）通通叫做類別成員。",[125,7637,7638,336,7641,7644,7645,7654,7655,7657,7658,7660,7661,7664],{},[15,7639,7640],{},"欄位（Fields）",[15,7642,7643],{},"純粹的死資料格子","。\n",[28,7646,7648],{"className":30,"code":7647,"language":32,"meta":33,"style":33},"public decimal TotalAmount; \u002F\u002F 👈 沒有加 { get; set; }！這就是欄位\n",[35,7649,7650],{"__ignoreMap":33},[38,7651,7652],{"class":40,"line":41},[38,7653,7647],{},"\n在物理上，它在 ",[15,7656,6676],{}," 的肉身托盤中就是一塊用來放二進位數字的空格（純記憶體空間）。它在中央廚房 ",[15,7659,6780],{}," 裡",[15,7662,7663],{},"沒有任何對應的 CPU 機器碼指令","。它的「數值」天天在變，但它身上沒有運算邏輯。",[20,7666,7668],{"id":7667},"_2-屬性properties披著變數外皮的偽裝方法","⚡ 2. 屬性（Properties）：披著變數外皮的「偽裝方法」",[28,7670,7672],{"className":30,"code":7671,"language":32,"meta":33,"style":33},"public decimal TotalAmount { get; set; } \u002F\u002F 👈 有加 { get; set; }！這叫屬性\n",[35,7673,7674],{"__ignoreMap":33},[38,7675,7676],{"class":40,"line":41},[38,7677,7671],{},[11,7679,7680,7681,7684,7685,142,7688],{},"屬性表面上看著像變數，但在編譯成機器碼後，編譯器會自動把它打散、生成兩個隱藏的方法：",[35,7682,7683],{},"get_TotalAmount()"," 和 ",[35,7686,7687],{},"set_TotalAmount()",[15,7689,7690],{},"屬性的本質是方法，也就是唯讀的 CPU 指令流！",[20,7692,7694],{"id":7693},"_3-為什麼欄位在物理上沒資格玩虛擬virtual","❌ 3. 為什麼欄位在物理上沒資格玩虛擬（Virtual）？",[11,7696,7697,7698,7700,7701,7703,7704,7707],{},"虛擬（",[35,7699,6727],{},"）與覆寫（",[35,7702,6731],{},"）的魔法，完全建立在 ",[15,7705,7706],{},"【Heap-Static】 點餐單表格（VMT \u002F 虛擬方法表）"," 的基礎上：",[387,7709,7710,7719,7728],{},[125,7711,7712,7715,7716,142],{},[15,7713,7714],{},"VMT 表格的硬體限制","：VMT 表格內的插槽，",[15,7717,7718],{},"只允許存放「CPU 唯讀運算指令（方法）的機房門牌地址」",[125,7720,7721,7724,7725,142],{},[15,7722,7723],{},"欄位沒有指令","：欄位只是 Heap-Heap 裡的一塊死肉身資料，它在 Heap-Static 裡根本沒有機器碼指令，因此",[15,7726,7727],{},"它在物理上絕對不可能、也沒資格在 VMT 點餐單上佔有任何一個插槽",[125,7729,7730,7733,7734,142],{},[15,7731,7732],{},"無電線可改接","：既然在 VMT 單子上連個插槽都沒有，兒子在寫代碼時，就",[15,7735,7736,7737,7739],{},"沒有任何多型電話線可以讓他用 ",[35,7738,6731],{}," 進行改接",[89,7741,7742],{},[11,7743,7744,7745,7748,7749,7751,7752,7754,7755,7757,7758,7761],{},"🎯 ",[15,7746,7747],{},"大一統物理結論：","\n程式碼（C# Code）只負責在 ",[15,7750,6780],{}," 開好空規格與格子；資料庫（DB）負責在執行期把真實數字灌入 ",[15,7753,6676],{}," 的大樓。\n虛擬（",[35,7756,6727],{},"）追求的「可變動」，是 ",[15,7759,7760],{},"VMT 點餐單上「運算邏輯（指令線路）」的可變動","。欄位肚子裡只有死資料、沒有指令線，因此欄位在物理上是絕對不可能成為虛擬的！",[272,7763],{},[85,7765,7767,7768,7770,7771,7774],{"id":7766},"九-進階多型控制new-同名陰陽線與-sealed-電焊槍","九、 進階多型控制：",[35,7769,1556],{}," 同名陰陽線與 ",[35,7772,7773],{},"sealed"," 電焊槍",[11,7776,7777,7778,7780,7781,7783],{},"微軟官方文件針對虛擬線路的控制，提供了 ",[35,7779,1556],{},"（隱藏）與 ",[35,7782,7773],{},"（密封）兩種更進階的硬體操作。",[20,7785,7787,7788,7790],{"id":7786},"_1-new-關鍵字method-hiding-同名陰陽線","🎭 1. ",[35,7789,1556],{}," 關鍵字（Method Hiding \u002F 同名陰陽線）",[122,7792,7793,7815],{},[125,7794,7795,7798,7799,7801,7802,7804,7805,7812,7813,407],{},[15,7796,7797],{},"物理本質","：它完全不碰老爸的 VMT 插槽！老爸在 VMT 上的插槽依然死死指著老爸的舊機房（",[35,7800,6790],{},"）。兒子使用 ",[35,7803,1556],{}," 只是",[15,7806,7807,7808,7811],{},"在自己的點餐單上，另外偷開了一個完全無關、剛好同名叫做 ",[35,7809,7810],{},"DoWork"," 的新插槽","，指著自己的新機房（",[35,7814,6784],{},[125,7816,7817,7820,7821],{},[15,7818,7819],{},"硬體走位（雙帳本機制）","：呼叫哪個方法，取決於「皮包（變數）的型別」，而不是「大樓肉身（實體）的型別」。\n",[28,7822,7824],{"className":30,"code":7823,"language":32,"meta":33,"style":33},"DerivedClass B = new();\nB.DoWork();  \u002F\u002F 翻兒子帳本 ➔ 執行兒子偷開的新機房 (0x2222)\n\nBaseClass A = B;\nA.DoWork();  \u002F\u002F 翻老爸帳本 ➔ 老爸原廠 VMT 插槽完好無損，執行老爸舊機房 (0x1111)！\n",[35,7825,7826,7831,7836,7840,7845],{"__ignoreMap":33},[38,7827,7828],{"class":40,"line":41},[38,7829,7830],{},"DerivedClass B = new();\n",[38,7832,7833],{"class":40,"line":47},[38,7834,7835],{},"B.DoWork();  \u002F\u002F 翻兒子帳本 ➔ 執行兒子偷開的新機房 (0x2222)\n",[38,7837,7838],{"class":40,"line":62},[38,7839,194],{"emptyLinePlaceholder":70},[38,7841,7842],{"class":40,"line":173},[38,7843,7844],{},"BaseClass A = B;\n",[38,7846,7847],{"class":40,"line":179},[38,7848,7849],{},"A.DoWork();  \u002F\u002F 翻老爸帳本 ➔ 老爸原廠 VMT 插槽完好無損，執行老爸舊機房 (0x1111)！\n",[20,7851,7853,7854,7857],{"id":7852},"_2-sealed-override密封覆寫-死死焊死","🔒 2. ",[35,7855,7856],{},"sealed override","（密封覆寫 \u002F 死死焊死）",[122,7859,7860,7886,7898],{},[125,7861,7862,7865,7866],{},[15,7863,7864],{},"實例演練","：繼承鏈條長這樣：老祖宗 A ➔ 爺爺 B ➔ 爸爸 C ➔ 兒子 D。\n",[122,7867,7868,7874,7880],{},[125,7869,7870,7871,7873],{},"老祖宗 A 挖了一個 ",[35,7872,6727],{}," 彈性插槽。",[125,7875,7876,7877,7879],{},"爺爺 B 寫了 ",[35,7878,6731],{},"，拿立可白改接電話線。",[125,7881,7882,7883,142],{},"到了爸爸 C 這一代，寫下了：",[35,7884,7885],{},"public sealed override void DoWork()",[125,7887,7888,7891,7892,7894,7895],{},[15,7889,7890],{},"物理動作","：這個 ",[35,7893,7773],{}," 在底層的物理動作就是：",[15,7896,7897],{},"爸爸 C 把電話線改接到自己的機房後，直接拿電焊槍把這個 VMT 插槽用鐵皮「死死焊大印焊死」！",[125,7899,7900,7903,7904,7906,7907,7909,7910,7912],{},[15,7901,7902],{},"後代的命運","：到了兒子 D 這一代，由於該插槽已被爸爸 C 用鐵皮焊死，不允許後代再拿立可白塗改（無法再寫 ",[35,7905,6731],{},"）。如果兒子 D 骨子裡極度反骨，非要自己寫一個同名的 ",[35,7908,7810],{},"，就只能退而求其次，加上 ",[35,7911,1556],{}," 關鍵字，自己在旁邊偷開一條同名的私人陰陽線。",[56,7914,58],{},{"title":33,"searchDepth":47,"depth":47,"links":7916},[7917,7918,7929,7934],{"id":6349,"depth":47,"text":6349},{"id":6378,"depth":47,"text":6379,"children":7919},[7920,7921,7922,7923,7924,7926,7928],{"id":6577,"depth":62,"text":6578},{"id":6685,"depth":62,"text":6686},{"id":6720,"depth":62,"text":6721},{"id":6761,"depth":62,"text":6762},{"id":6833,"depth":62,"text":7925},"五、 揭秘 base 關鍵字：多型宇宙的超高速傳送門",{"id":6929,"depth":62,"text":7927},"六、 幕後黑魔法：世界上根本沒有「實體方法」與隱藏的 this",{"id":7106,"depth":62,"text":7107},{"id":7615,"depth":47,"text":7616,"children":7930},[7931,7932,7933],{"id":7626,"depth":62,"text":7627},{"id":7667,"depth":62,"text":7668},{"id":7693,"depth":62,"text":7694},{"id":7766,"depth":47,"text":7935,"children":7936},"九、 進階多型控制：new 同名陰陽線與 sealed 電焊槍",[7937,7939],{"id":7786,"depth":62,"text":7938},"🎭 1. new 關鍵字（Method Hiding \u002F 同名陰陽線）",{"id":7852,"depth":62,"text":7940},"🔒 2. sealed override（密封覆寫 \u002F 死死焊死）",{},{"title":6299,"description":6306},"C#\u002F物件\u002F物件 03 多型性.md","notes\u002Fsync\u002Fcsharp-物件-物件-03-多型性",[76,4954],"eKs0qQYiZ2sHlyj9EtvOaeSfGEzAn0cJ7vorG5iXSQs",{"id":7948,"title":7949,"body":7950,"category":64,"date":4138,"description":7954,"draft":67,"extension":68,"meta":8202,"navigation":70,"path":8203,"seo":8204,"sourcePath":8205,"stem":8206,"tags":8207,"__hash__":8208},"notes\u002Fnotes\u002Fsync\u002Fcsharp-物件-物件object.md","物件Object",{"type":8,"value":7951,"toc":8194},[7952,7955,7958,7962,7965,7977,7981,7984,7992,8021,8026,8044,8089,8093,8096,8120,8124,8190,8192],[11,7953,7954],{},"雖然在js跟cshsarp都叫 object，而且在各自的語言裡都隱約有「代表全部」的身影，但在底層的物理結構與生存哲學上，兩者有著天翻地覆的差別。",[85,7956,7957],{"id":7957},"簡介",[20,7959,7961],{"id":7960},"_1-javascript-的-object-沒有地基隨時能變形的百寶袋","1. JavaScript 的 object ——「沒有地基、隨時能變形的百寶袋」",[11,7963,7964],{},"在 JavaScript 裡，object 幾乎代表了全部\n（除了基本型別以外，連 Array、Function 底層都是 object）。",[122,7966,7967,7974],{},[125,7968,7969,7970,7973],{},"物理真相：JS 的物件在底層（V8引擎）本質上是一個",[35,7971,7972],{},"動態的雜湊表","（Hash Table \u002F Map）。它是一個空的百寶袋。",[125,7975,7976],{},"生存哲學：它沒有固定的藍圖（Class）。你隨時可以用 obj.age = 25 憑空塞一個新欄位進去。它代表「全部」，是因為它像黏土一樣，你捏成什麼，它就是什麼。",[20,7978,7980],{"id":7979},"_2-c-的-object-鐵打的萬物之源最模糊的最高祖先","2. C# 的 object ——「鐵打的萬物之源、最模糊的最高祖先」",[11,7982,7983],{},"在 C# 的世界裡，object（也就是系統底層的 System.Object）的代表全部，完全是另一種物理邏輯。C# 是強型別語言，講究的是「長幼尊卑的嚴格繼承樹」。",[122,7985,7986,7989],{},[125,7987,7988],{},"在 C# 中，object 代表全部的意思是：「它是全天下所有型別共同的、最頂層的唯一祖先（至高無上的父類別）。」",[125,7990,7991],{},"你在 C# 裡寫的任何自訂類別，就算你什麼都不寫，C# 在編譯時都會強迫繼承 object：",[28,7993,7995],{"className":30,"code":7994,"language":32,"meta":33,"style":33},"\u002F\u002F 你以為你只是寫了一個簡單的類別：\npublic class Mammal { }\n\n\u002F\u002F 但在電腦眼裡，物理上它其實長這樣（自動繼承）：\npublic class Mammal : System.Object { }\n",[35,7996,7997,8002,8007,8011,8016],{"__ignoreMap":33},[38,7998,7999],{"class":40,"line":41},[38,8000,8001],{},"\u002F\u002F 你以為你只是寫了一個簡單的類別：\n",[38,8003,8004],{"class":40,"line":47},[38,8005,8006],{},"public class Mammal { }\n",[38,8008,8009],{"class":40,"line":62},[38,8010,194],{"emptyLinePlaceholder":70},[38,8012,8013],{"class":40,"line":173},[38,8014,8015],{},"\u002F\u002F 但在電腦眼裡，物理上它其實長這樣（自動繼承）：\n",[38,8017,8018],{"class":40,"line":179},[38,8019,8020],{},"public class Mammal : System.Object { }\n",[89,8022,8023],{},[11,8024,8025],{},"object boxedMammal = new Mammal() 為什麼可以合理存在?\n因為海獅骨子裡一定也是個 object。",[122,8027,8028],{},[125,8029,8030,8031,8034,8035,8034,8038,8034,8041],{},"4 個最基本的功能\n*    ",[35,8032,8033],{},"ToString()","\n*    ",[35,8036,8037],{},"Equals(object obj)",[35,8039,8040],{},"GetType()",[35,8042,8043],{},"GetHashCode()",[28,8045,8047],{"className":4987,"code":8046,"language":4989,"meta":33,"style":33},"【boxedMammal 的操作介面】\n ┌────────────────────────┐\n ├─ Equals()              │ ───> 點得出來（老祖宗給的）\n ├─ GetHashCode()         │ ───> 點得出來（老祖宗給的）\n ├─ GetType()             │ ───> 點得出來（老祖宗給的）\n ├─ ToString()            │ ───> 點得出來（老祖宗給的）\n └────────────────────────┘\n ❌ .Name ───> 「查無此功能！」因為老祖宗根本不知道什麼是 Name。\n",[35,8048,8049,8054,8059,8064,8069,8074,8079,8084],{"__ignoreMap":33},[38,8050,8051],{"class":40,"line":41},[38,8052,8053],{},"【boxedMammal 的操作介面】\n",[38,8055,8056],{"class":40,"line":47},[38,8057,8058],{}," ┌────────────────────────┐\n",[38,8060,8061],{"class":40,"line":62},[38,8062,8063],{}," ├─ Equals()              │ ───> 點得出來（老祖宗給的）\n",[38,8065,8066],{"class":40,"line":173},[38,8067,8068],{}," ├─ GetHashCode()         │ ───> 點得出來（老祖宗給的）\n",[38,8070,8071],{"class":40,"line":179},[38,8072,8073],{}," ├─ GetType()             │ ───> 點得出來（老祖宗給的）\n",[38,8075,8076],{"class":40,"line":185},[38,8077,8078],{}," ├─ ToString()            │ ───> 點得出來（老祖宗給的）\n",[38,8080,8081],{"class":40,"line":191},[38,8082,8083],{}," └────────────────────────┘\n",[38,8085,8086],{"class":40,"line":197},[38,8087,8088],{}," ❌ .Name ───> 「查無此功能！」因為老祖宗根本不知道什麼是 Name。\n",[20,8090,8092],{"id":8091},"_3-c-的-object-裝進去之後會集體失憶","3. C# 的 object 裝進去之後會「集體失憶」",[11,8094,8095],{},"在 JavaScript 裡，你把物件宣告成什麼都無所謂，你隨時能點出裡面的屬性。\n但在 C# 裡，當你把海獅（Mammal）的地址塞進一個 object 變數時，你手裡的放大鏡規格就被降格到了最模糊的 object 級別：",[28,8097,8099],{"className":30,"code":8098,"language":32,"meta":33,"style":33},"object boxedMammal = new Mammal { Name = \"Sea lion\" };\n\n\u002F\u002F ❌ 在 C# 裡這行絕對連編譯都不會過！\nConsole.WriteLine(boxedMammal.Name);\n",[35,8100,8101,8106,8110,8115],{"__ignoreMap":33},[38,8102,8103],{"class":40,"line":41},[38,8104,8105],{},"object boxedMammal = new Mammal { Name = \"Sea lion\" };\n",[38,8107,8108],{"class":40,"line":47},[38,8109,194],{"emptyLinePlaceholder":70},[38,8111,8112],{"class":40,"line":62},[38,8113,8114],{},"\u002F\u002F ❌ 在 C# 裡這行絕對連編譯都不會過！\n",[38,8116,8117],{"class":40,"line":173},[38,8118,8119],{},"Console.WriteLine(boxedMammal.Name);\n",[20,8121,8123],{"id":8122},"_4-兩者在記憶體與觀念上的終極對照","4. 兩者在記憶體與觀念上的終極對照",[28,8125,8127],{"className":4987,"code":8126,"language":4989,"meta":33,"style":33},"\n【JavaScript 的 object (動態黏土)】\n變數 obj ───> [ 記憶體百寶袋 ] ───> 隨時可以 .name = \"A\", .age = 10，完全自由。\n\n\n【C# 的 object (嚴格的繼承樹最頂端)】\n                 System.Object (老祖宗：只會 ToString, GetType)\n                       │\n         ┌─────────────┴─────────────┐\n      Mammal (水獺藍圖)            Reptile (蜥蜴藍圖)\n    (.Name 寫在這裡)\n\n 變數 object boxed ───> [ 拿著最頂層的合約 ] ───> 雖然指著 Mammal 實體，但強制不准讀取 .Name！\n\n",[35,8128,8129,8133,8138,8143,8147,8151,8156,8161,8166,8171,8176,8181,8185],{"__ignoreMap":33},[38,8130,8131],{"class":40,"line":41},[38,8132,194],{"emptyLinePlaceholder":70},[38,8134,8135],{"class":40,"line":47},[38,8136,8137],{},"【JavaScript 的 object (動態黏土)】\n",[38,8139,8140],{"class":40,"line":62},[38,8141,8142],{},"變數 obj ───> [ 記憶體百寶袋 ] ───> 隨時可以 .name = \"A\", .age = 10，完全自由。\n",[38,8144,8145],{"class":40,"line":173},[38,8146,194],{"emptyLinePlaceholder":70},[38,8148,8149],{"class":40,"line":179},[38,8150,194],{"emptyLinePlaceholder":70},[38,8152,8153],{"class":40,"line":185},[38,8154,8155],{},"【C# 的 object (嚴格的繼承樹最頂端)】\n",[38,8157,8158],{"class":40,"line":191},[38,8159,8160],{},"                 System.Object (老祖宗：只會 ToString, GetType)\n",[38,8162,8163],{"class":40,"line":197},[38,8164,8165],{},"                       │\n",[38,8167,8168],{"class":40,"line":203},[38,8169,8170],{},"         ┌─────────────┴─────────────┐\n",[38,8172,8173],{"class":40,"line":209},[38,8174,8175],{},"      Mammal (水獺藍圖)            Reptile (蜥蜴藍圖)\n",[38,8177,8178],{"class":40,"line":215},[38,8179,8180],{},"    (.Name 寫在這裡)\n",[38,8182,8183],{"class":40,"line":221},[38,8184,194],{"emptyLinePlaceholder":70},[38,8186,8187],{"class":40,"line":227},[38,8188,8189],{}," 變數 object boxed ───> [ 拿著最頂層的合約 ] ───> 雖然指著 Mammal 實體，但強制不准讀取 .Name！\n",[11,8191,3837],{},[56,8193,58],{},{"title":33,"searchDepth":47,"depth":47,"links":8195},[8196],{"id":7957,"depth":47,"text":7957,"children":8197},[8198,8199,8200,8201],{"id":7960,"depth":62,"text":7961},{"id":7979,"depth":62,"text":7980},{"id":8091,"depth":62,"text":8092},{"id":8122,"depth":62,"text":8123},{},"\u002Fnotes\u002Fsync\u002Fcsharp-object",{"title":7949,"description":7954},"C#\u002F物件\u002F物件Object.md","notes\u002Fsync\u002Fcsharp-物件-物件object",[76,4954],"RvwMkKdf5cnYDW-VPq_W6WlAgEzakWEORNIEcjsS0ig",{"id":8210,"title":8211,"body":8212,"category":64,"date":4138,"description":2986,"draft":67,"extension":68,"meta":8595,"navigation":70,"path":8596,"seo":8597,"sourcePath":8598,"stem":8599,"tags":8600,"__hash__":8601},"notes\u002Fnotes\u002Fsync\u002Fcsharp-開發-resoucehub-resourcescontroller-cs.md","ResourcesController.cs",{"type":8,"value":8213,"toc":8586},[8214,8381,8391,8399,8413,8416,8443,8451,8486,8492,8503,8512,8533,8539,8544,8550,8555,8560,8575,8584],[28,8215,8217],{"className":30,"code":8216,"language":32,"meta":33,"style":33},"using Microsoft.EntityFrameworkCore;\nusing Microsoft.AspNetCore.Mvc;\nusing ResourceHub.Data;\nusing ResourceHub.Models;\n\nnamespace ResourceHub.Controllers\n{\n\n    [Route(\"api\u002F[controller]\")]\n    [ApiController]\n    public class ResourcesController(AppDbContext context) : ControllerBase\n    {\n\n        [HttpGet]\n        public async Task\u003CActionResult\u003CIEnumerable\u003CResource>>> GetResources()\n        {\n            return await context.Resources.ToListAsync();\n        }\n\n        [HttpPost]\n        public async Task\u003CActionResult\u003CResource>> PostResource(Resource resource)\n        {\n\n            \u002F\u002F 2. 直接使用 context\n            context.Resources.Add(resource);\n\n            await context.SaveChangesAsync();\n\n            return CreatedAtAction(\n                nameof(GetResources), \n                new { id = resource.Id }, resource\n            );\n        }\n    }\n}\n\n",[35,8218,8219,8223,8228,8232,8237,8241,8246,8250,8254,8259,8264,8269,8274,8278,8283,8288,8293,8298,8303,8307,8312,8317,8321,8325,8330,8335,8339,8344,8348,8353,8358,8363,8368,8372,8377],{"__ignoreMap":33},[38,8220,8221],{"class":40,"line":41},[38,8222,2681],{},[38,8224,8225],{"class":40,"line":47},[38,8226,8227],{},"using Microsoft.AspNetCore.Mvc;\n",[38,8229,8230],{"class":40,"line":62},[38,8231,2686],{},[38,8233,8234],{"class":40,"line":173},[38,8235,8236],{},"using ResourceHub.Models;\n",[38,8238,8239],{"class":40,"line":179},[38,8240,194],{"emptyLinePlaceholder":70},[38,8242,8243],{"class":40,"line":185},[38,8244,8245],{},"namespace ResourceHub.Controllers\n",[38,8247,8248],{"class":40,"line":191},[38,8249,165],{},[38,8251,8252],{"class":40,"line":197},[38,8253,194],{"emptyLinePlaceholder":70},[38,8255,8256],{"class":40,"line":203},[38,8257,8258],{},"    [Route(\"api\u002F[controller]\")]\n",[38,8260,8261],{"class":40,"line":209},[38,8262,8263],{},"    [ApiController]\n",[38,8265,8266],{"class":40,"line":215},[38,8267,8268],{},"    public class ResourcesController(AppDbContext context) : ControllerBase\n",[38,8270,8271],{"class":40,"line":221},[38,8272,8273],{},"    {\n",[38,8275,8276],{"class":40,"line":227},[38,8277,194],{"emptyLinePlaceholder":70},[38,8279,8280],{"class":40,"line":232},[38,8281,8282],{},"        [HttpGet]\n",[38,8284,8285],{"class":40,"line":238},[38,8286,8287],{},"        public async Task\u003CActionResult\u003CIEnumerable\u003CResource>>> GetResources()\n",[38,8289,8290],{"class":40,"line":244},[38,8291,8292],{},"        {\n",[38,8294,8295],{"class":40,"line":249},[38,8296,8297],{},"            return await context.Resources.ToListAsync();\n",[38,8299,8300],{"class":40,"line":255},[38,8301,8302],{},"        }\n",[38,8304,8305],{"class":40,"line":261},[38,8306,194],{"emptyLinePlaceholder":70},[38,8308,8309],{"class":40,"line":267},[38,8310,8311],{},"        [HttpPost]\n",[38,8313,8314],{"class":40,"line":1264},[38,8315,8316],{},"        public async Task\u003CActionResult\u003CResource>> PostResource(Resource resource)\n",[38,8318,8319],{"class":40,"line":1269},[38,8320,8292],{},[38,8322,8323],{"class":40,"line":3116},[38,8324,194],{"emptyLinePlaceholder":70},[38,8326,8327],{"class":40,"line":3121},[38,8328,8329],{},"            \u002F\u002F 2. 直接使用 context\n",[38,8331,8332],{"class":40,"line":3126},[38,8333,8334],{},"            context.Resources.Add(resource);\n",[38,8336,8337],{"class":40,"line":3132},[38,8338,194],{"emptyLinePlaceholder":70},[38,8340,8341],{"class":40,"line":3138},[38,8342,8343],{},"            await context.SaveChangesAsync();\n",[38,8345,8346],{"class":40,"line":3143},[38,8347,194],{"emptyLinePlaceholder":70},[38,8349,8350],{"class":40,"line":3149},[38,8351,8352],{},"            return CreatedAtAction(\n",[38,8354,8355],{"class":40,"line":3155},[38,8356,8357],{},"                nameof(GetResources), \n",[38,8359,8360],{"class":40,"line":3161},[38,8361,8362],{},"                new { id = resource.Id }, resource\n",[38,8364,8365],{"class":40,"line":3167},[38,8366,8367],{},"            );\n",[38,8369,8370],{"class":40,"line":3172},[38,8371,8302],{},[38,8373,8374],{"class":40,"line":3177},[38,8375,8376],{},"    }\n",[38,8378,8379],{"class":40,"line":3183},[38,8380,270],{},[20,8382,8384,2567,8387,8390],{"id":8383},"route-與-apicontroller-是什麼",[35,8385,8386],{},"[Route]",[35,8388,8389],{},"[ApiController]"," 是什麼？",[89,8392,8393],{},[11,8394,8395,8396],{},"在 C# 中稱為 ",[15,8397,8398],{},"「特性 (Attribute)」",[11,8400,8401,8402,8405,8406,8409,8410,407],{},"對應到 JavaScript \u002F TypeScript \u002F Angular 的世界，它就完完全全是 ",[15,8403,8404],{},"「裝飾器 (Decorator)」","（例如 Angular 的 ",[35,8407,8408],{},"@Component"," 或 NestJS 的 ",[35,8411,8412],{},"@Controller",[11,8414,8415],{},"做底層的自動化設定：",[122,8417,8418,8425],{},[125,8419,8420,8424],{},[15,8421,8422],{},[35,8423,8389],{},"：告訴 .NET 說「這是一個 API 控制器，不是用來回傳 HTML 網頁的」。加上它之後，.NET 會自動幫你做很多事，例如：當前端傳來的 JSON 格式不對時，它會自動攔截並回傳 HTTP 400 (Bad Request)，你連檢查的程式碼都不用寫。",[125,8426,8427,8432,8433,8436,8437,8439,8440,142],{},[15,8428,8429],{},[35,8430,8431],{},"[Route(\"api\u002F[controller]\")]","：設定這支 API 的網址路徑。裡面的 ",[35,8434,8435],{},"[controller]"," 是一個動態變數，.NET 會自動抓取這個類別的名稱（去掉 \"Controller\" 字尾）。因為你的類別叫 ",[35,8438,2888],{},"，所以這支 API 的路由就會自動變成 ",[35,8441,8442],{},"api\u002FResources",[20,8444,8446,2567,8448],{"id":8445},"controllerbase-與-aspnetcoremvc",[35,8447,2884],{},[35,8449,8450],{},"AspNetCore.Mvc",[122,8452,8453,8459],{},[125,8454,8455,8458],{},[35,8456,8457],{},"ResourcesController : ControllerBase"," 代表繼承。",[125,8460,8461,8463,8464],{},[35,8462,2884],{}," 是微軟寫好的基礎類別，裡面包含了所有寫 API 需要用到的工具。例如\n",[122,8465,8466,8471,8476,8481],{},[125,8467,8468],{},[35,8469,8470],{},"CreatedAtAction()",[125,8472,8473],{},[35,8474,8475],{},"Ok()",[125,8477,8478],{},[35,8479,8480],{},"NotFound()",[125,8482,8483],{},[35,8484,8485],{},"BadRequest()",[20,8487,8489],{"id":8488},"task",[35,8490,8491],{},"Task",[89,8493,8494],{},[11,8495,8496,8497,8499,8500],{},"C# 裡的 ",[35,8498,8491],{},"，完完全全就等於 JS 裡的 ",[35,8501,8502],{},"Promise",[11,8504,8505,8506,8509,8510,142],{},"在 C# 裡只要你的方法使用了 ",[35,8507,8508],{},"async","，它的回傳型別就必須是 ",[35,8511,8491],{},[122,8513,8514,8524],{},[125,8515,8516,8517,8520,8521,8523],{},"JS 的 ",[35,8518,8519],{},"Promise\u003Cvoid>"," 👉 C# 的 ",[35,8522,8491],{},"（沒有回傳值，只等它做完）",[125,8525,8516,8526,8520,8529,8532],{},[35,8527,8528],{},"Promise\u003CT>",[35,8530,8531],{},"Task\u003CT>","（有回傳值）",[20,8534,8536],{"id":8535},"actionresult",[35,8537,8538],{},"ActionResult",[89,8540,8541],{},[11,8542,8543],{},"HTTP 回應包裝盒",[11,8545,8546,8547,142],{},"ASP.NET Core 提供的一個特殊型別，它允許這個方法",[15,8548,8549],{},"不只能回傳資料，還能回傳 HTTP 狀態碼",[20,8551,8553],{"id":8552},"ienumerable",[35,8554,284],{},[89,8556,8557],{},[11,8558,8559],{},"可疊代的集合",[11,8561,8562,8563,8568,8569,142,8572,8574],{},"在 C# 裡，只要看到大寫的 ",[15,8564,8565],{},[35,8566,8567],{},"I"," 開頭，就代表它是一個 ",[15,8570,8571],{},"介面 (Interface)",[35,8573,284],{}," 的意思是「這是一個可以被逐一讀取 (列舉\u002F疊代) 的集合」。",[11,8576,8577,8580,8581,8583],{},[15,8578,8579],{},"為什麼不用 List 或 Array？"," 因為 ",[35,8582,284],{}," 是一種「抽象」的保證。它告訴呼叫端：「我會給你一包 Resource，你可以用迴圈 (foreach) 把裡面的東西拿出來，但你不需要管我底層是用陣列還是清單實作的。」這種寫法在 C# 中被認為是耦合度最低、最優雅的標準寫法。",[56,8585,58],{},{"title":33,"searchDepth":47,"depth":47,"links":8587},[8588,8590,8592,8593,8594],{"id":8383,"depth":62,"text":8589},"[Route] 與 [ApiController] 是什麼？",{"id":8445,"depth":62,"text":8591},"ControllerBase 與 AspNetCore.Mvc",{"id":8488,"depth":62,"text":8491},{"id":8535,"depth":62,"text":8538},{"id":8552,"depth":62,"text":284},{},"\u002Fnotes\u002Fsync\u002Fcsharp-resoucehub-resourcescontroller-cs",{"title":8211,"description":2986},"C#\u002F開發\u002FResouceHub\u002FResourcesController.cs.md","notes\u002Fsync\u002Fcsharp-開發-resoucehub-resourcescontroller-cs",[76,2993],"tdl1_J6pDRE4s1hTJ5xcr4uhRVP7P-65C_QM2fuaOtU",{"id":8603,"title":8604,"body":8605,"category":64,"date":4138,"description":8638,"draft":67,"extension":68,"meta":8639,"navigation":70,"path":8640,"seo":8641,"sourcePath":8642,"stem":8643,"tags":8644,"__hash__":8645},"notes\u002Fnotes\u002Fsync\u002Fcsharp-開發-resoucehub-開發經驗.md","開發經驗",{"type":8,"value":8606,"toc":8635},[8607,8611,8615,8624,8633],[20,8608,8610],{"id":8609},"_1-甚麼是getset","1. 甚麼是{get;set;}",[608,8612,8614],{"id":8613},"欄位-field-vs-屬性-property","欄位 (Field) vs. 屬性 (Property)",[28,8616,8618],{"className":30,"code":8617,"language":32,"meta":33,"style":33},"public string Title; \u002F\u002F 這是「欄位 (Field)」，就像 JS 裡的一般變數\n",[35,8619,8620],{"__ignoreMap":33},[38,8621,8622],{"class":40,"line":41},[38,8623,8617],{},[28,8625,8627],{"className":30,"code":8626,"language":32,"meta":33,"style":33},"public string Title { get; set; } \u002F\u002F 這是「屬性 (Property)」\n",[35,8628,8629],{"__ignoreMap":33},[38,8630,8631],{"class":40,"line":41},[38,8632,8626],{},[56,8634,58],{},{"title":33,"searchDepth":47,"depth":47,"links":8636},[8637],{"id":8609,"depth":62,"text":8610},"public string Title; \u002F\u002F 這是「欄位 (Field)」，就像 JS 裡的一般變數",{},"\u002Fnotes\u002Fsync\u002Fcsharp-resoucehub",{"title":8604,"description":8638},"C#\u002F開發\u002FResouceHub\u002F開發經驗.md","notes\u002Fsync\u002Fcsharp-開發-resoucehub-開發經驗",[76,2993],"vRT5Gl8VSKMAY_ST_jJVDyfFt6WBXkAmSG03a6gEZ2g",{"id":8647,"title":8648,"body":8649,"category":64,"date":4138,"description":9250,"draft":67,"extension":68,"meta":9251,"navigation":70,"path":2661,"seo":9252,"sourcePath":9253,"stem":9254,"tags":9255,"__hash__":9256},"notes\u002Fnotes\u002Fsync\u002Fcsharp-開發-主流開發流程-初學.md","主流開發流程(初學)",{"type":8,"value":8650,"toc":9240},[8651,8656,8659,8663,8748,8750,8754,8758,8768,8776,8827,8831,8836,8843,8896,8900,8909,8916,9067,9072,9076,9085,9091,9205,9209,9212,9215,9227,9237],[8652,8653,8655],"h1",{"id":8654},"c-aspnet-core-web-api-開發指南resource-hub","C# ASP.NET Core Web API 開發指南：Resource Hub",[11,8657,8658],{},"這是一份針對現代 C# Web API 開發的標準流程指南。採用業界最主流的「由底而上 (Data-Driven)」開發模式，完美契合「關注點分離」原則。",[85,8660,8662],{"id":8661},"️-開發環境準備","🛠️ 開發環境準備",[122,8664,8665,8672,8683],{},[125,8666,8667,8668,8671],{},"安裝 ",[15,8669,8670],{},".NET 8.0 SDK"," (或更新版本)",[125,8673,8674,8675,8678,8679,8682],{},"編輯器使用 ",[15,8676,8677],{},"VS Code","，並安裝官方 ",[15,8680,8681],{},"C# Dev Kit"," 擴充套件",[125,8684,8685,8686],{},"開啟終端機執行初始化指令：\n",[28,8687,8691],{"className":8688,"code":8689,"language":8690,"meta":33,"style":33},"language-bash shiki shiki-themes github-light github-dark","dotnet new webapi -n ResourceHub --use-controllers\ncd ResourceHub\ndotnet add package Microsoft.EntityFrameworkCore\ndotnet add package Microsoft.EntityFrameworkCore.InMemory\n","bash",[35,8692,8693,8716,8724,8737],{"__ignoreMap":33},[38,8694,8695,8699,8703,8706,8710,8713],{"class":40,"line":41},[38,8696,8698],{"class":8697},"sScJk","dotnet",[38,8700,8702],{"class":8701},"sZZnC"," new",[38,8704,8705],{"class":8701}," webapi",[38,8707,8709],{"class":8708},"sj4cs"," -n",[38,8711,8712],{"class":8701}," ResourceHub",[38,8714,8715],{"class":8708}," --use-controllers\n",[38,8717,8718,8721],{"class":40,"line":47},[38,8719,8720],{"class":8708},"cd",[38,8722,8723],{"class":8701}," ResourceHub\n",[38,8725,8726,8728,8731,8734],{"class":40,"line":62},[38,8727,8698],{"class":8697},[38,8729,8730],{"class":8701}," add",[38,8732,8733],{"class":8701}," package",[38,8735,8736],{"class":8701}," Microsoft.EntityFrameworkCore\n",[38,8738,8739,8741,8743,8745],{"class":40,"line":173},[38,8740,8698],{"class":8697},[38,8742,8730],{"class":8701},[38,8744,8733],{"class":8701},[38,8746,8747],{"class":8701}," Microsoft.EntityFrameworkCore.InMemory\n",[272,8749],{},[85,8751,8753],{"id":8752},"標準開發五步驟-bottom-up","🚀 標準開發五步驟 (Bottom-Up)",[20,8755,8757],{"id":8756},"步驟-1定義資料模型-models","步驟 1：定義資料模型 (Models)",[11,8759,8760,8763,8764,8767],{},[15,8761,8762],{},"概念對應："," 類似 TypeScript 的 ",[35,8765,8766],{},"interface","。\n先確定資料長什麼樣子，建立藍圖。",[11,8769,8770,505,8773],{},[15,8771,8772],{},"路徑：",[35,8774,8775],{},"Models\u002FResource.cs",[28,8777,8779],{"className":30,"code":8778,"language":32,"meta":33,"style":33},"namespace ResourceHub.Models\n{\n    public class Resource\n    {\n        public int Id { get; set; }\n        public string Title { get; set; } = string.Empty;\n        public string Url { get; set; } = string.Empty;\n        public string Category { get; set; } = string.Empty;\n    }\n}\n",[35,8780,8781,8786,8790,8795,8799,8804,8809,8814,8819,8823],{"__ignoreMap":33},[38,8782,8783],{"class":40,"line":41},[38,8784,8785],{},"namespace ResourceHub.Models\n",[38,8787,8788],{"class":40,"line":47},[38,8789,165],{},[38,8791,8792],{"class":40,"line":62},[38,8793,8794],{},"    public class Resource\n",[38,8796,8797],{"class":40,"line":173},[38,8798,176],{},[38,8800,8801],{"class":40,"line":179},[38,8802,8803],{},"        public int Id { get; set; }\n",[38,8805,8806],{"class":40,"line":185},[38,8807,8808],{},"        public string Title { get; set; } = string.Empty;\n",[38,8810,8811],{"class":40,"line":191},[38,8812,8813],{},"        public string Url { get; set; } = string.Empty;\n",[38,8815,8816],{"class":40,"line":197},[38,8817,8818],{},"        public string Category { get; set; } = string.Empty;\n",[38,8820,8821],{"class":40,"line":203},[38,8822,264],{},[38,8824,8825],{"class":40,"line":209},[38,8826,270],{},[20,8828,8830],{"id":8829},"步驟-2建立資料庫上下文-dbcontext","步驟 2：建立資料庫上下文 (DbContext)",[11,8832,8833,8835],{},[15,8834,8762],{}," 負責與資料庫溝通的服務層。\n引入 Entity Framework Core，讓程式碼知道如何把物件轉成資料表。",[11,8837,8838,505,8840],{},[15,8839,8772],{},[35,8841,8842],{},"Data\u002FAppDbContext.cs",[28,8844,8846],{"className":30,"code":8845,"language":32,"meta":33,"style":33},"using Microsoft.EntityFrameworkCore;\nusing ResourceHub.Models;\n\nnamespace ResourceHub.Data\n{\n    public class AppDbContext : DbContext\n    {\n        public AppDbContext(DbContextOptions\u003CAppDbContext> options) : base(options) { }\n        public DbSet\u003CResource> Resources { get; set; }\n    }\n}\n",[35,8847,8848,8852,8856,8860,8865,8869,8874,8878,8883,8888,8892],{"__ignoreMap":33},[38,8849,8850],{"class":40,"line":41},[38,8851,2681],{},[38,8853,8854],{"class":40,"line":47},[38,8855,8236],{},[38,8857,8858],{"class":40,"line":62},[38,8859,194],{"emptyLinePlaceholder":70},[38,8861,8862],{"class":40,"line":173},[38,8863,8864],{},"namespace ResourceHub.Data\n",[38,8866,8867],{"class":40,"line":179},[38,8868,165],{},[38,8870,8871],{"class":40,"line":185},[38,8872,8873],{},"    public class AppDbContext : DbContext\n",[38,8875,8876],{"class":40,"line":191},[38,8877,176],{},[38,8879,8880],{"class":40,"line":197},[38,8881,8882],{},"        public AppDbContext(DbContextOptions\u003CAppDbContext> options) : base(options) { }\n",[38,8884,8885],{"class":40,"line":203},[38,8886,8887],{},"        public DbSet\u003CResource> Resources { get; set; }\n",[38,8889,8890],{"class":40,"line":209},[38,8891,264],{},[38,8893,8894],{"class":40,"line":215},[38,8895,270],{},[20,8897,8899],{"id":8898},"步驟-3撰寫-api-控制器-controllers","步驟 3：撰寫 API 控制器 (Controllers)",[11,8901,8902,8904,8905,8908],{},[15,8903,8762],{}," 類似 Next.js 的 ",[35,8906,8907],{},"route.ts"," 或 NestJS 的 Controller。\n開放 HTTP 接口，透過依賴注入呼叫資料庫，並回傳 JSON 結果。",[11,8910,8911,505,8913],{},[15,8912,8772],{},[35,8914,8915],{},"Controllers\u002FResourcesController.cs",[28,8917,8919],{"className":30,"code":8918,"language":32,"meta":33,"style":33},"using Microsoft.AspNetCore.Mvc;\nusing Microsoft.EntityFrameworkCore;\nusing ResourceHub.Data;\nusing ResourceHub.Models;\n\nnamespace ResourceHub.Controllers\n{\n    [Route(\"api\u002F[controller]\")]\n    [ApiController]\n    public class ResourcesController : ControllerBase\n    {\n        private readonly AppDbContext _context;\n\n        public ResourcesController(AppDbContext context)\n        {\n            _context = context;\n        }\n\n        [HttpGet]\n        public async Task\u003CActionResult\u003CIEnumerable\u003CResource>>> GetResources()\n        {\n            return await _context.Resources.ToListAsync();\n        }\n\n        [HttpPost]\n        public async Task\u003CActionResult\u003CResource>> PostResource(Resource resource)\n        {\n            _context.Resources.Add(resource);\n            await _context.SaveChangesAsync();\n            return CreatedAtAction(nameof(GetResources), new { id = resource.Id }, resource);\n        }\n    }\n}\n",[35,8920,8921,8925,8929,8933,8937,8941,8945,8949,8954,8959,8964,8968,8973,8977,8982,8986,8991,8995,8999,9004,9009,9013,9018,9022,9026,9031,9036,9040,9045,9050,9055,9059,9063],{"__ignoreMap":33},[38,8922,8923],{"class":40,"line":41},[38,8924,8227],{},[38,8926,8927],{"class":40,"line":47},[38,8928,2681],{},[38,8930,8931],{"class":40,"line":62},[38,8932,2686],{},[38,8934,8935],{"class":40,"line":173},[38,8936,8236],{},[38,8938,8939],{"class":40,"line":179},[38,8940,194],{"emptyLinePlaceholder":70},[38,8942,8943],{"class":40,"line":185},[38,8944,8245],{},[38,8946,8947],{"class":40,"line":191},[38,8948,165],{},[38,8950,8951],{"class":40,"line":197},[38,8952,8953],{},"    [Route(\"api\u002F[controller]\")]\n",[38,8955,8956],{"class":40,"line":203},[38,8957,8958],{},"    [ApiController]\n",[38,8960,8961],{"class":40,"line":209},[38,8962,8963],{},"    public class ResourcesController : ControllerBase\n",[38,8965,8966],{"class":40,"line":215},[38,8967,176],{},[38,8969,8970],{"class":40,"line":221},[38,8971,8972],{},"        private readonly AppDbContext _context;\n",[38,8974,8975],{"class":40,"line":227},[38,8976,194],{"emptyLinePlaceholder":70},[38,8978,8979],{"class":40,"line":232},[38,8980,8981],{},"        public ResourcesController(AppDbContext context)\n",[38,8983,8984],{"class":40,"line":238},[38,8985,212],{},[38,8987,8988],{"class":40,"line":244},[38,8989,8990],{},"            _context = context;\n",[38,8992,8993],{"class":40,"line":249},[38,8994,224],{},[38,8996,8997],{"class":40,"line":255},[38,8998,194],{"emptyLinePlaceholder":70},[38,9000,9001],{"class":40,"line":261},[38,9002,9003],{},"        [HttpGet]\n",[38,9005,9006],{"class":40,"line":267},[38,9007,9008],{},"        public async Task\u003CActionResult\u003CIEnumerable\u003CResource>>> GetResources()\n",[38,9010,9011],{"class":40,"line":1264},[38,9012,212],{},[38,9014,9015],{"class":40,"line":1269},[38,9016,9017],{},"            return await _context.Resources.ToListAsync();\n",[38,9019,9020],{"class":40,"line":3116},[38,9021,224],{},[38,9023,9024],{"class":40,"line":3121},[38,9025,194],{"emptyLinePlaceholder":70},[38,9027,9028],{"class":40,"line":3126},[38,9029,9030],{},"        [HttpPost]\n",[38,9032,9033],{"class":40,"line":3132},[38,9034,9035],{},"        public async Task\u003CActionResult\u003CResource>> PostResource(Resource resource)\n",[38,9037,9038],{"class":40,"line":3138},[38,9039,212],{},[38,9041,9042],{"class":40,"line":3143},[38,9043,9044],{},"            _context.Resources.Add(resource);\n",[38,9046,9047],{"class":40,"line":3149},[38,9048,9049],{},"            await _context.SaveChangesAsync();\n",[38,9051,9052],{"class":40,"line":3155},[38,9053,9054],{},"            return CreatedAtAction(nameof(GetResources), new { id = resource.Id }, resource);\n",[38,9056,9057],{"class":40,"line":3161},[38,9058,224],{},[38,9060,9061],{"class":40,"line":3167},[38,9062,264],{},[38,9064,9065],{"class":40,"line":3172},[38,9066,270],{},[11,9068,9069],{},[1347,9070,9071],{},"(備註：為保持版面簡潔，此處僅展示 GET 與 POST，實務上可補齊 PUT 與 DELETE)",[20,9073,9075],{"id":9074},"步驟-4依賴注入與配置-programcs","步驟 4：依賴注入與配置 (Program.cs)",[11,9077,9078,9080,9081,9084],{},[15,9079,8762],{}," 類似 Angular 的 ",[35,9082,9083],{},"app.config.ts"," 總管中心。\n將寫好的控制器與記憶體資料庫註冊到應用程式中。",[11,9086,9087,505,9089],{},[15,9088,8772],{},[35,9090,2669],{},[28,9092,9094],{"className":30,"code":9093,"language":32,"meta":33,"style":33},"using Microsoft.EntityFrameworkCore;\nusing ResourceHub.Data;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n\u002F\u002F --- 核心註冊區 ---\nbuilder.Services.AddControllers(); \nbuilder.Services.AddDbContext\u003CAppDbContext>(options =>\n    options.UseInMemoryDatabase(\"ResourceDb\"));\n\u002F\u002F ----------------\n\nbuilder.Services.AddEndpointsApiExplorer();\nbuilder.Services.AddSwaggerGen();\n\nvar app = builder.Build();\n\nif (app.Environment.IsDevelopment())\n{\n    app.UseSwagger();\n    app.UseSwaggerUI();\n}\n\napp.UseHttpsRedirection();\napp.MapControllers(); \napp.Run();\n",[35,9095,9096,9100,9104,9108,9112,9116,9121,9126,9130,9135,9140,9144,9149,9154,9158,9162,9166,9170,9174,9179,9184,9188,9192,9196,9201],{"__ignoreMap":33},[38,9097,9098],{"class":40,"line":41},[38,9099,2681],{},[38,9101,9102],{"class":40,"line":47},[38,9103,2686],{},[38,9105,9106],{"class":40,"line":62},[38,9107,194],{"emptyLinePlaceholder":70},[38,9109,9110],{"class":40,"line":173},[38,9111,2695],{},[38,9113,9114],{"class":40,"line":179},[38,9115,194],{"emptyLinePlaceholder":70},[38,9117,9118],{"class":40,"line":185},[38,9119,9120],{},"\u002F\u002F --- 核心註冊區 ---\n",[38,9122,9123],{"class":40,"line":191},[38,9124,9125],{},"builder.Services.AddControllers(); \n",[38,9127,9128],{"class":40,"line":197},[38,9129,2709],{},[38,9131,9132],{"class":40,"line":203},[38,9133,9134],{},"    options.UseInMemoryDatabase(\"ResourceDb\"));\n",[38,9136,9137],{"class":40,"line":209},[38,9138,9139],{},"\u002F\u002F ----------------\n",[38,9141,9142],{"class":40,"line":215},[38,9143,194],{"emptyLinePlaceholder":70},[38,9145,9146],{"class":40,"line":221},[38,9147,9148],{},"builder.Services.AddEndpointsApiExplorer();\n",[38,9150,9151],{"class":40,"line":227},[38,9152,9153],{},"builder.Services.AddSwaggerGen();\n",[38,9155,9156],{"class":40,"line":232},[38,9157,194],{"emptyLinePlaceholder":70},[38,9159,9160],{"class":40,"line":238},[38,9161,2728],{},[38,9163,9164],{"class":40,"line":244},[38,9165,194],{"emptyLinePlaceholder":70},[38,9167,9168],{"class":40,"line":249},[38,9169,2742],{},[38,9171,9172],{"class":40,"line":255},[38,9173,165],{},[38,9175,9176],{"class":40,"line":261},[38,9177,9178],{},"    app.UseSwagger();\n",[38,9180,9181],{"class":40,"line":267},[38,9182,9183],{},"    app.UseSwaggerUI();\n",[38,9185,9186],{"class":40,"line":1264},[38,9187,270],{},[38,9189,9190],{"class":40,"line":1269},[38,9191,194],{"emptyLinePlaceholder":70},[38,9193,9194],{"class":40,"line":3116},[38,9195,2764],{},[38,9197,9198],{"class":40,"line":3121},[38,9199,9200],{},"app.MapControllers(); \n",[38,9202,9203],{"class":40,"line":3126},[38,9204,2779],{},[20,9206,9208],{"id":9207},"步驟-5啟動與驗證","步驟 5：啟動與驗證",[11,9210,9211],{},"完成所有配置後，啟動伺服器並使用內建的 Swagger UI 進行 API 測試。",[11,9213,9214],{},"在 VS Code 終端機執行：",[28,9216,9218],{"className":8688,"code":9217,"language":8690,"meta":33,"style":33},"dotnet run\n",[35,9219,9220],{"__ignoreMap":33},[38,9221,9222,9224],{"class":40,"line":41},[38,9223,8698],{"class":8697},[38,9225,9226],{"class":8701}," run\n",[11,9228,9229,9232,9233,9236],{},[15,9230,9231],{},"驗收："," 打開瀏覽器前往 ",[35,9234,9235],{},"http:\u002F\u002Flocalhost:\u003C你的Port>\u002Fswagger","，即可開始發送 API 請求！",[56,9238,9239],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":33,"searchDepth":47,"depth":47,"links":9241},[9242,9243],{"id":8661,"depth":47,"text":8662},{"id":8752,"depth":47,"text":8753,"children":9244},[9245,9246,9247,9248,9249],{"id":8756,"depth":62,"text":8757},{"id":8829,"depth":62,"text":8830},{"id":8898,"depth":62,"text":8899},{"id":9074,"depth":62,"text":9075},{"id":9207,"depth":62,"text":9208},"這是一份針對現代 C Web API 開發的標準流程指南。採用業界最主流的「由底而上 (Data-Driven)」開發模式，完美契合「關注點分離」原則。",{},{"title":8648,"description":9250},"C#\u002F開發\u002F主流開發流程(初學).md","notes\u002Fsync\u002Fcsharp-開發-主流開發流程-初學",[76,2993],"WN-FYhot6ZfC09E9v7ewFWadpw3P0BqNTEniOjlLVdk",{"id":9258,"title":9259,"body":9260,"category":9331,"date":9332,"description":9333,"draft":67,"extension":68,"meta":9334,"navigation":70,"path":9335,"seo":9336,"sourcePath":2646,"stem":9337,"tags":9338,"__hash__":9341},"notes\u002Fnotes\u002Fschema-driven-forms.md","Schema-Driven 表單思路",{"type":8,"value":9261,"toc":9324},[9262,9265,9268,9271,9274,9296,9299,9302,9313,9316,9319,9321],[85,9263,9264],{"id":9264},"問題",[11,9266,9267],{},"當表單欄位一多，畫面、驗證、預設值常散落在元件各處，改一個規則要改好幾層。",[85,9269,9270],{"id":9270},"做法",[11,9272,9273],{},"把欄位定義收成 schema，例如：",[122,9275,9276,9287,9293],{},[125,9277,9278,642,9281,642,9284],{},[35,9279,9280],{},"name",[35,9282,9283],{},"type",[35,9285,9286],{},"label",[125,9288,9289,9292],{},[35,9290,9291],{},"required","、驗證規則",[125,9294,9295],{},"預設值與顯示條件",[11,9297,9298],{},"UI 只負責「依 schema 渲染」，業務規則盡量留在 schema 或對應的 parser。",[85,9300,9301],{"id":9301},"適合什麼時候",[122,9303,9304,9307,9310],{},[125,9305,9306],{},"欄位結構常變（後台、設定頁、標案表單）",[125,9308,9309],{},"同一套欄位要在多個流程重用",[125,9311,9312],{},"希望驗證與文案可被非 UI 層共用",[85,9314,9315],{"id":9315},"代價",[11,9317,9318],{},"多一層抽象：簡單三欄表單不必上 schema。schema 本身也需要維護與型別約束。",[85,9320,2621],{"id":2621},[11,9322,9323],{},"欄位一多、規則一複，就把「長什麼樣子」和「怎麼驗證」從畫面元件抽出去。Schema-driven 不是銀彈，但是複雜表單的好預設。",{"title":33,"searchDepth":47,"depth":47,"links":9325},[9326,9327,9328,9329,9330],{"id":9264,"depth":47,"text":9264},{"id":9270,"depth":47,"text":9270},{"id":9301,"depth":47,"text":9301},{"id":9315,"depth":47,"text":9315},{"id":2621,"depth":47,"text":2621},"other","2026-08-05","用 schema 描述欄位與驗證，讓複雜表單的結構、文案與規則集中管理。",{},"\u002Fnotes\u002Fschema-driven-forms",{"title":9259,"description":9333},"notes\u002Fschema-driven-forms",[9339,9340],"Forms","Architecture","Of3jmcSLEfvz7bWDJOGIun7OJ0TrqVEM9rzI_vfrYMI",{"id":9343,"title":9344,"body":9345,"category":2640,"date":9332,"description":9725,"draft":67,"extension":68,"meta":9726,"navigation":70,"path":9727,"seo":9728,"sourcePath":9729,"stem":9730,"tags":9731,"__hash__":9732},"notes\u002Fnotes\u002Fsync\u002Fvue-01-creating-a-vue-application.md","Creating a Vue Application",{"type":8,"value":9346,"toc":9714},[9347,9359,9390,9392,9395,9423,9425,9432,9439,9448,9452,9455,9460,9466,9481,9487,9512,9517,9542,9544,9551,9559,9573,9579,9581,9585,9598,9600,9604,9612,9623,9626,9640,9642,9646,9651,9697,9710,9712],[85,9348,9350,9351,9354,9355,9358],{"id":9349},"每個-vue-application都是-createapp-函数创建","每個 ",[35,9352,9353],{},"Vue Application","都是 ",[35,9356,9357],{},"createApp"," 函数创建",[28,9360,9364],{"className":9361,"code":9362,"language":9363,"meta":33,"style":33},"language-javascript shiki shiki-themes github-light github-dark","import { createApp } from 'vue'\n\nconst app = createApp({\n  \u002F* root component options *\u002F\n})\n","javascript",[35,9365,9366,9371,9375,9380,9385],{"__ignoreMap":33},[38,9367,9368],{"class":40,"line":41},[38,9369,9370],{},"import { createApp } from 'vue'\n",[38,9372,9373],{"class":40,"line":47},[38,9374,194],{"emptyLinePlaceholder":70},[38,9376,9377],{"class":40,"line":62},[38,9378,9379],{},"const app = createApp({\n",[38,9381,9382],{"class":40,"line":173},[38,9383,9384],{},"  \u002F* root component options *\u002F\n",[38,9386,9387],{"class":40,"line":179},[38,9388,9389],{},"})\n",[272,9391],{},[85,9393,9394],{"id":9394},"每個應用都需要根組件",[28,9396,9398],{"className":9361,"code":9397,"language":9363,"meta":33,"style":33},"import { createApp } from 'vue'\n\u002F\u002F import the root component App from a single-file component.\nimport App from '.\u002FApp.vue'\n\nconst app = createApp(App)\n",[35,9399,9400,9404,9409,9414,9418],{"__ignoreMap":33},[38,9401,9402],{"class":40,"line":41},[38,9403,9370],{},[38,9405,9406],{"class":40,"line":47},[38,9407,9408],{},"\u002F\u002F import the root component App from a single-file component.\n",[38,9410,9411],{"class":40,"line":62},[38,9412,9413],{},"import App from '.\u002FApp.vue'\n",[38,9415,9416],{"class":40,"line":173},[38,9417,194],{"emptyLinePlaceholder":70},[38,9419,9420],{"class":40,"line":179},[38,9421,9422],{},"const app = createApp(App)\n",[272,9424],{},[85,9426,9428,9429],{"id":9427},"掛載-application","掛載 ",[35,9430,9431],{},"Application",[11,9433,9434,9435,9438],{},"Application只有在調用",[35,9436,9437],{},".mount()","方法后才会渲染任何内容",[28,9440,9442],{"className":9361,"code":9441,"language":9363,"meta":33,"style":33},"app.mount('#app')\n",[35,9443,9444],{"__ignoreMap":33},[38,9445,9446],{"class":40,"line":41},[38,9447,9441],{},[20,9449,9451],{"id":9450},"container-不屬於-vue-application-的一部分","Container 不屬於 Vue Application 的一部分",[11,9453,9454],{},"Vue 的魔法（如綁定變數、監聽點擊），對這個最外層的 HTML 標籤是完全無效的",[387,9456,9457],{},[125,9458,9459],{},"渲染前（你的專案檔案）",[11,9461,2855,9462,9465],{},[35,9463,9464],{},"index.html"," (這是 Container 容器)",[28,9467,9469],{"className":9361,"code":9468,"language":9363,"meta":33,"style":33},"\u003C!-- Vue 管不到這裡！如果你在這裡寫 Vue 的語法，瀏覽器會看不懂 --> \n\u003Cdiv id=\"app\" {{ msg }} @click=\"doSomething\">\u003C\u002Fdiv>\n",[35,9470,9471,9476],{"__ignoreMap":33},[38,9472,9473],{"class":40,"line":41},[38,9474,9475],{},"\u003C!-- Vue 管不到這裡！如果你在這裡寫 Vue 的語法，瀏覽器會看不懂 --> \n",[38,9477,9478],{"class":40,"line":47},[38,9479,9480],{},"\u003Cdiv id=\"app\" {{ msg }} @click=\"doSomething\">\u003C\u002Fdiv>\n",[11,9482,2855,9483,9486],{},[35,9484,9485],{},"App.vue"," (這是 Root Component 根元件)",[28,9488,9490],{"className":9361,"code":9489,"language":9363,"meta":33,"style":33},"\u003Ctemplate>\n  \u003C!-- Vue 只管得到這裡面 -->\n  \u003Ch1>我是 App.vue 裡面的標題\u003C\u002Fh1>\n\u003C\u002Ftemplate>\n",[35,9491,9492,9497,9502,9507],{"__ignoreMap":33},[38,9493,9494],{"class":40,"line":41},[38,9495,9496],{},"\u003Ctemplate>\n",[38,9498,9499],{"class":40,"line":47},[38,9500,9501],{},"  \u003C!-- Vue 只管得到這裡面 -->\n",[38,9503,9504],{"class":40,"line":62},[38,9505,9506],{},"  \u003Ch1>我是 App.vue 裡面的標題\u003C\u002Fh1>\n",[38,9508,9509],{"class":40,"line":173},[38,9510,9511],{},"\u003C\u002Ftemplate>\n",[387,9513,9514],{"start":47},[125,9515,9516],{},"渲染後（瀏覽器最終看到的樣子）",[28,9518,9520],{"className":9361,"code":9519,"language":9363,"meta":33,"style":33},"\u003Cdiv id=\"app\"> \n    \u003C!-- 從這裡開始，才是 Vue 真正有掌控權的「應用程式內部」 --> \n    \u003Ch1>我是 App.vue 裡面的標題\u003C\u002Fh1> \n\u003C\u002Fdiv>\n",[35,9521,9522,9527,9532,9537],{"__ignoreMap":33},[38,9523,9524],{"class":40,"line":41},[38,9525,9526],{},"\u003Cdiv id=\"app\"> \n",[38,9528,9529],{"class":40,"line":47},[38,9530,9531],{},"    \u003C!-- 從這裡開始，才是 Vue 真正有掌控權的「應用程式內部」 --> \n",[38,9533,9534],{"class":40,"line":62},[38,9535,9536],{},"    \u003Ch1>我是 App.vue 裡面的標題\u003C\u002Fh1> \n",[38,9538,9539],{"class":40,"line":173},[38,9540,9541],{},"\u003C\u002Fdiv>\n",[11,9543,4182],{},[608,9545,9547,9550],{"id":9546},"mount是最後的啟動開關",[35,9548,9549],{},"mount()","是最後的啟動開關",[11,9552,9553,9555,9556,9558],{},[35,9554,9437],{}," 方法應該始終在所有應用程式配置和資源註冊完成後呼叫，一旦呼叫了 ",[35,9557,9437],{},"，Vue 就會開始解析畫面並渲染到瀏覽器上。",[122,9560,9561,9564,9567],{},[125,9562,9563],{},"路由設定 (Vue Router)",[125,9565,9566],{},"狀態管理 (Pinia)",[125,9568,9569,9570,7024],{},"全域共用的 UI 元件 (如你提到的 ",[35,9571,9572],{},"TodoDeleteButton",[11,9574,9575,9576,9578],{},"以上都需要在 ",[35,9577,9437],{},"之前完成。",[11,9580,4182],{},[608,9582,9584],{"id":9583},"回傳值的本質不同-實體-vs-元件","回傳值的本質不同 (實體 vs 元件)",[122,9586,9587,9593],{},[125,9588,9589,9592],{},[35,9590,9591],{},"createApp()"," 回傳的是「應用程式實體 (Application Instance)」",[125,9594,9595,9597],{},[35,9596,9437],{}," 回傳的是「根元件實體 (Component Instance)」",[272,9599],{},[20,9601,9603],{"id":9602},"app-configurations","App Configurations",[89,9605,9606],{},[11,9607,9608,9609],{},"它設定的是 ",[15,9610,9611],{},"Vue 框架本身的底層行為",[11,9613,9614,9615,9618,9619,9622],{},"當你寫下 ",[35,9616,9617],{},"const app = createApp(App)","的時候，會提供一個",[35,9620,9621],{},"app.config","調整全域的系統機制。",[11,9624,9625],{},"最常見的只有這幾個：",[122,9627,9628,9634],{},[125,9629,9630,9631,7024],{},"全域錯誤處理 (",[35,9632,9633],{},"app.config.errorHandler",[125,9635,9636,9637,7024],{},"全域變數 (",[35,9638,9639],{},"app.config.globalProperties",[272,9641],{},[20,9643,9645],{"id":9644},"multiple-application-instances","Multiple Application Instances",[89,9647,9648],{},[11,9649,9650],{},"createApp API 允許多個 Vue 應用程式共存於相同頁面",[28,9652,9654],{"className":9361,"code":9653,"language":9363,"meta":33,"style":33},"const app1 = createApp({\n  \u002F* ... *\u002F\n})\napp1.mount('#container-1')\n\nconst app2 = createApp({\n  \u002F* ... *\u002F\n})\napp2.mount('#container-2')\n",[35,9655,9656,9661,9666,9670,9675,9679,9684,9688,9692],{"__ignoreMap":33},[38,9657,9658],{"class":40,"line":41},[38,9659,9660],{},"const app1 = createApp({\n",[38,9662,9663],{"class":40,"line":47},[38,9664,9665],{},"  \u002F* ... *\u002F\n",[38,9667,9668],{"class":40,"line":62},[38,9669,9389],{},[38,9671,9672],{"class":40,"line":173},[38,9673,9674],{},"app1.mount('#container-1')\n",[38,9676,9677],{"class":40,"line":179},[38,9678,194],{"emptyLinePlaceholder":70},[38,9680,9681],{"class":40,"line":185},[38,9682,9683],{},"const app2 = createApp({\n",[38,9685,9686],{"class":40,"line":191},[38,9687,9665],{},[38,9689,9690],{"class":40,"line":197},[38,9691,9389],{},[38,9693,9694],{"class":40,"line":203},[38,9695,9696],{},"app2.mount('#container-2')\n",[122,9698,9699],{},[125,9700,9701,9702,9705,9706,9709],{},"是在解決",[15,9703,9704],{},"舊有系統翻新","或",[15,9707,9708],{},"混合架構","的真實開發痛點",[272,9711],{},[56,9713,58],{},{"title":33,"searchDepth":47,"depth":47,"links":9715},[9716,9718,9719],{"id":9349,"depth":47,"text":9717},"每個 Vue Application都是 createApp 函数创建",{"id":9394,"depth":47,"text":9394},{"id":9427,"depth":47,"text":9720,"children":9721},"掛載 Application",[9722,9723,9724],{"id":9450,"depth":62,"text":9451},{"id":9602,"depth":62,"text":9603},{"id":9644,"depth":62,"text":9645},"import { createApp } from 'vue'",{},"\u002Fnotes\u002Fsync\u002Fvue-01-creating-a-vue-application",{"title":9344,"description":9725},"Vue\u002F01-Creating a Vue Application.md","notes\u002Fsync\u002Fvue-01-creating-a-vue-application",[2649],"Fu7IEQszAOj5ddJUS9dN2r--NrqeohOjiq7kZzZ1J0s",{"id":9734,"title":9735,"body":9736,"category":2640,"date":9332,"description":10328,"draft":67,"extension":68,"meta":10329,"navigation":70,"path":10330,"seo":10331,"sourcePath":10332,"stem":10333,"tags":10334,"__hash__":10335},"notes\u002Fnotes\u002Fsync\u002Fvue-02-template-syntax.md","Template Syntax",{"type":8,"value":9737,"toc":10308},[9738,9742,9746,9756,9760,9766,9769,9773,9782,9784,9788,9792,9815,9818,9822,9827,9836,9839,9843,9872,9875,9884,9888,9891,9928,9935,9937,9941,9946,9950,9975,10012,10043,10047,10056,10099,10103,10117,10121,10160,10163,10178,10201,10205,10219,10248,10251,10294,10297,10306],[85,9739,9741],{"id":9740},"vue-運作的核心機制","Vue 運作的核心機制",[20,9743,9745],{"id":9744},"編譯為-js-compile","編譯為 JS (Compile)：",[11,9747,9748,9749,9752,9753],{},"Vue 在建置階段（或執行初期），會在底層把你寫的 ",[35,9750,9751],{},"\u003Ctemplate>"," 全部轉換成高效率的 ",[15,9754,9755],{},"JavaScript 渲染函式 (Render Function)",[20,9757,9759],{"id":9758},"精準打擊-minimal-dom-manipulations","精準打擊 (Minimal DOM manipulations)：",[11,9761,9762,9763,142],{},"搭配 Vue 的虛擬 DOM (Virtual DOM) ，當你的變數改變時，會在記憶體中計算出新舊畫面的「最小差異」，然後",[15,9764,9765],{},"只去更新真實網頁上那一個微小的節點",[11,9767,9768],{},"=====",[85,9770,9772],{"id":9771},"text-interpolation-文字插植","Text Interpolation (文字插植)",[28,9774,9776],{"className":9361,"code":9775,"language":9363,"meta":33,"style":33},"\u003Cspan>Message: {{ msg }}\u003C\u002Fspan>\n",[35,9777,9778],{"__ignoreMap":33},[38,9779,9780],{"class":40,"line":41},[38,9781,9775],{},[11,9783,9768],{},[85,9785,9787],{"id":9786},"raw-html","Raw HTML",[20,9789,9791],{"id":9790},"text-interpolation-文字插值-vs-raw-html-原始-html","Text Interpolation (文字插值) vs Raw HTML (原始 HTML)",[122,9793,9794,9801],{},[125,9795,9796,9797,9800],{},"用文字差值的方式",[35,9798,9799],{},"{{}}","，可以杜絕===XSS 攻擊===",[125,9802,9803,9804,9807,9808,9811,9812,9814],{},"永遠只在「你完全信任且能控制」的內容上使用 ",[35,9805,9806],{},"v-html","（例如你自己公司 CMS 後台編輯器產生的文章）。",[15,9809,9810],{},"絕對不要","把使用者輸入的內容，直接用 ",[35,9813,9806],{}," 渲染在畫面上。",[11,9816,9817],{},"======",[85,9819,9821],{"id":9820},"attribute-bindings-屬性綁定","Attribute Bindings (屬性綁定)",[89,9823,9824],{},[11,9825,9826],{},"不能在 HTML 屬性中使用花括號。請改用 v-bind 指令",[28,9828,9830],{"className":9361,"code":9829,"language":9363,"meta":33,"style":33},"\u003Cdiv v-bind:id=\"dynamicId\">\u003C\u002Fdiv>\n",[35,9831,9832],{"__ignoreMap":33},[38,9833,9834],{"class":40,"line":41},[38,9835,9829],{},[20,9837,9838],{"id":9838},"簡寫",[9840,9841],"div",{":id":9842},"dynamicId",[28,9844,9846],{"className":9361,"code":9845,"language":9363,"meta":33,"style":33},"const objectOfAttrs = {\n  id: 'container',\n  class: 'wrapper',\n  style: 'background-color:green'\n}\n",[35,9847,9848,9853,9858,9863,9868],{"__ignoreMap":33},[38,9849,9850],{"class":40,"line":41},[38,9851,9852],{},"const objectOfAttrs = {\n",[38,9854,9855],{"class":40,"line":47},[38,9856,9857],{},"  id: 'container',\n",[38,9859,9860],{"class":40,"line":62},[38,9861,9862],{},"  class: 'wrapper',\n",[38,9864,9865],{"class":40,"line":173},[38,9866,9867],{},"  style: 'background-color:green'\n",[38,9869,9870],{"class":40,"line":179},[38,9871,270],{},[11,9873,9874],{},"搭配",[28,9876,9878],{"className":9361,"code":9877,"language":9363,"meta":33,"style":33},"\u003Cdiv v-bind=\"objectOfAttrs\">\u003C\u002Fdiv>\n",[35,9879,9880],{"__ignoreMap":33},[38,9881,9882],{"class":40,"line":41},[38,9883,9877],{},[20,9885,9887],{"id":9886},"javascript-表達式-expression","JavaScript 表達式 (Expression)",[11,9889,9890],{},"文件差值內部可以接受JS的表達式",[28,9892,9894],{"className":9361,"code":9893,"language":9363,"meta":33,"style":33},"{{ number + 1 }}\n\n{{ ok ? 'YES' : 'NO' }}\n\n{{ message.split('').reverse().join('') }}\n\n\u003Cdiv :id=\"`list-${id}`\">\u003C\u002Fdiv>\n",[35,9895,9896,9901,9905,9910,9914,9919,9923],{"__ignoreMap":33},[38,9897,9898],{"class":40,"line":41},[38,9899,9900],{},"{{ number + 1 }}\n",[38,9902,9903],{"class":40,"line":47},[38,9904,194],{"emptyLinePlaceholder":70},[38,9906,9907],{"class":40,"line":62},[38,9908,9909],{},"{{ ok ? 'YES' : 'NO' }}\n",[38,9911,9912],{"class":40,"line":173},[38,9913,194],{"emptyLinePlaceholder":70},[38,9915,9916],{"class":40,"line":179},[38,9917,9918],{},"{{ message.split('').reverse().join('') }}\n",[38,9920,9921],{"class":40,"line":185},[38,9922,194],{"emptyLinePlaceholder":70},[38,9924,9925],{"class":40,"line":191},[38,9926,9927],{},"\u003Cdiv :id=\"`list-${id}`\">\u003C\u002Fdiv>\n",[11,9929,9930,9931,9934],{},"但是",[15,9932,9933],{},"不能","放用來「宣告」或「控制流程」的陳述式",[272,9936],{},[85,9938,9940],{"id":9939},"directives","Directives",[89,9942,9943],{},[11,9944,9945],{},"帶有==v-==前綴的特殊屬性。",[20,9947,9949],{"id":9948},"arguments","Arguments",[122,9951,9952,9969],{},[125,9953,9954,9955,9958,9959,322,9962,9965,9966,142],{},"指令 (Directive) 就是 ",[35,9956,9957],{},"v-"," 開頭的字（例如 ",[35,9960,9961],{},"v-bind",[35,9963,9964],{},"v-on","）。但是，光說指令是不夠的，Vue 需要知道",[15,9967,9968],{},"這個指令要作用在誰身上",[125,9970,9971,9974],{},[15,9972,9973],{},"公式","：指令:參數=\"表達式\"",[28,9976,9978],{"className":9361,"code":9977,"language":9363,"meta":33,"style":33},"\u003Ca v-bind:href=\"url\">\n\n\u003Ca :href=\"url\">               \u002F\u002F 縮寫\n\n\u003Ca v-on:click=\"doSomething\">\n\n\u003Ca @click=\"doSomething\">      \u002F\u002F 縮寫\n",[35,9979,9980,9985,9989,9994,9998,10003,10007],{"__ignoreMap":33},[38,9981,9982],{"class":40,"line":41},[38,9983,9984],{},"\u003Ca v-bind:href=\"url\">\n",[38,9986,9987],{"class":40,"line":47},[38,9988,194],{"emptyLinePlaceholder":70},[38,9990,9991],{"class":40,"line":62},[38,9992,9993],{},"\u003Ca :href=\"url\">               \u002F\u002F 縮寫\n",[38,9995,9996],{"class":40,"line":173},[38,9997,194],{"emptyLinePlaceholder":70},[38,9999,10000],{"class":40,"line":179},[38,10001,10002],{},"\u003Ca v-on:click=\"doSomething\">\n",[38,10004,10005],{"class":40,"line":185},[38,10006,194],{"emptyLinePlaceholder":70},[38,10008,10009],{"class":40,"line":191},[38,10010,10011],{},"\u003Ca @click=\"doSomething\">      \u002F\u002F 縮寫\n",[89,10013,10014],{},[11,10015,10016,10020,10021,10026,10027,10030,10031,10033,10034,10039,10040,10042],{},[15,10017,10018],{},[35,10019,9961],{}," 是指令（告訴 Vue：幫我動態綁定一個屬性）\n",[15,10022,10023],{},[35,10024,10025],{},"href"," 就是這段文件所說的 ",[15,10028,10029],{},"「參數 (Argument)」","（告訴 Vue：我要綁定的是 ",[35,10032,10025],{}," 這個屬性）\n",[15,10035,10036],{},[35,10037,10038],{},"url"," 是表達式（告訴 Vue：這個屬性的值來自於 ",[35,10041,10038],{}," 這個變數）",[20,10044,10046],{"id":10045},"dynamic-arguments","Dynamic Arguments",[28,10048,10050],{"className":9361,"code":10049,"language":9363,"meta":33,"style":33},"\u003Ca :[attributeName]=\"url\"> ... \u003C\u002Fa>\n",[35,10051,10052],{"__ignoreMap":33},[38,10053,10054],{"class":40,"line":41},[38,10055,10049],{},[122,10057,10058,10073,10076,10090],{},[125,10059,10060,10061,10064,10065,10068,10069,10072],{},"消滅因 ",[15,10062,10063],{},"『屬性名稱不同"," 或 ",[15,10066,10067],{},"『事件名稱不同』"," 而產生的 ",[35,10070,10071],{},"v-if"," 複製貼上地獄。",[125,10074,10075],{},"有時候要綁定 A 屬性\u002F事件，有時候要綁定 B 屬性\u002F事件",[125,10077,10078,10079,10082,10083,10086,10087,142],{},"日常開發 95% 的業務邏輯中，",[15,10080,10081],{},"真的用不到它","。我們通常都很明確知道一個按鈕要綁定 ",[35,10084,10085],{},"click","，一個輸入框要綁定 ",[35,10088,10089],{},"disabled",[125,10091,10092,10093,10064,10096,142],{},"主要服務於 ",[15,10094,10095],{},"「開發底層共用元件庫（如 shadcn、ElementUI）」",[15,10097,10098],{},"「極度複雜的資料驅動架構（Schema-Driven）」",[20,10100,10102],{"id":10101},"情境一開發一個萬用的下拉選單-dropdown","情境一：開發一個萬用的「下拉選單 (Dropdown)」",[11,10104,10105,10106,10109,10110,10113,10114,10116],{},"假設你要幫公司寫一個共用的 ",[35,10107,10108],{},"\u003CAppDropdown>"," 元件。 設計師要求：在「電腦版」時，滑鼠移過去（",[35,10111,10112],{},"mouseenter","）就要展開；在「手機版」時，必須點擊（",[35,10115,10085],{},"）才會展開。",[608,10118,10120],{"id":10119},"如果沒有動態參數","如果沒有動態參數：",[28,10122,10124],{"className":9361,"code":10123,"language":9363,"meta":33,"style":33},"\u003C!-- 逼不得已的寫法：把所有可能的事件都綁定，然後在邏輯裡判斷 -->\n\u003Cdiv \n  @mouseenter=\"isDesktop ? open() : null\"\n  @click=\"isMobile ? open() : null\"\n>\n  點我或滑我\n\u003C\u002Fdiv>\n",[35,10125,10126,10131,10136,10141,10146,10151,10156],{"__ignoreMap":33},[38,10127,10128],{"class":40,"line":41},[38,10129,10130],{},"\u003C!-- 逼不得已的寫法：把所有可能的事件都綁定，然後在邏輯裡判斷 -->\n",[38,10132,10133],{"class":40,"line":47},[38,10134,10135],{},"\u003Cdiv \n",[38,10137,10138],{"class":40,"line":62},[38,10139,10140],{},"  @mouseenter=\"isDesktop ? open() : null\"\n",[38,10142,10143],{"class":40,"line":173},[38,10144,10145],{},"  @click=\"isMobile ? open() : null\"\n",[38,10147,10148],{"class":40,"line":179},[38,10149,10150],{},">\n",[38,10152,10153],{"class":40,"line":185},[38,10154,10155],{},"  點我或滑我\n",[38,10157,10158],{"class":40,"line":191},[38,10159,9541],{},[608,10161,10162],{"id":10162},"使用動態參數",[28,10164,10166],{"className":9361,"code":10165,"language":9363,"meta":33,"style":33},"\u002F\u002F JS 裡判斷：如果是手機就回傳 'click'，否則回傳 'mouseenter'\nconst triggerEvent = computed(() => isMobile.value ? 'click' : 'mouseenter');\n",[35,10167,10168,10173],{"__ignoreMap":33},[38,10169,10170],{"class":40,"line":41},[38,10171,10172],{},"\u002F\u002F JS 裡判斷：如果是手機就回傳 'click'，否則回傳 'mouseenter'\n",[38,10174,10175],{"class":40,"line":47},[38,10176,10177],{},"const triggerEvent = computed(() => isMobile.value ? 'click' : 'mouseenter');\n",[28,10179,10181],{"className":9361,"code":10180,"language":9363,"meta":33,"style":33},"\u003C!-- 乾淨俐落！ -->\n\u003Cdiv @[triggerEvent]=\"open()\">\n  點我或滑我\n\u003C\u002Fdiv>\n",[35,10182,10183,10188,10193,10197],{"__ignoreMap":33},[38,10184,10185],{"class":40,"line":41},[38,10186,10187],{},"\u003C!-- 乾淨俐落！ -->\n",[38,10189,10190],{"class":40,"line":47},[38,10191,10192],{},"\u003Cdiv @[triggerEvent]=\"open()\">\n",[38,10194,10195],{"class":40,"line":62},[38,10196,10155],{},[38,10198,10199],{"class":40,"line":173},[38,10200,9541],{},[20,10202,10204],{"id":10203},"情境二後端決定前端的驗證屬性schema-driven","情境二：後端決定前端的驗證屬性（Schema-Driven）",[11,10206,10207,10208,10211,10212,10215,10216,407],{},"假設後端傳來一包 JSON，告訴你這個 ",[35,10209,10210],{},"\u003Cinput>"," 有一個長度或大小的限制。 有時候是限制「字數」（屬性叫 ",[35,10213,10214],{},"maxlength","），有時候是限制「數字大小」（屬性叫 ",[35,10217,10218],{},"max",[28,10220,10222],{"className":9361,"code":10221,"language":9363,"meta":33,"style":33},"\u002F\u002F 後端傳來的設定\nconst fieldRule = {\n  limitType: 'maxlength', \u002F\u002F 下次可能會變成 'max'\n  limitValue: 20\n}\n",[35,10223,10224,10229,10234,10239,10244],{"__ignoreMap":33},[38,10225,10226],{"class":40,"line":41},[38,10227,10228],{},"\u002F\u002F 後端傳來的設定\n",[38,10230,10231],{"class":40,"line":47},[38,10232,10233],{},"const fieldRule = {\n",[38,10235,10236],{"class":40,"line":62},[38,10237,10238],{},"  limitType: 'maxlength', \u002F\u002F 下次可能會變成 'max'\n",[38,10240,10241],{"class":40,"line":173},[38,10242,10243],{},"  limitValue: 20\n",[38,10245,10246],{"class":40,"line":179},[38,10247,270],{},[608,10249,10120],{"id":10250},"如果沒有動態參數-1",[28,10252,10254],{"className":9361,"code":10253,"language":9363,"meta":33,"style":33},"\u003Cinput \n  v-if=\"fieldRule.limitType === 'maxlength'\" \n  :maxlength=\"fieldRule.limitValue\" \n\u002F>\n\u003Cinput \n  v-if=\"fieldRule.limitType === 'max'\" \n  :max=\"fieldRule.limitValue\" \n\u002F>\n",[35,10255,10256,10261,10266,10271,10276,10280,10285,10290],{"__ignoreMap":33},[38,10257,10258],{"class":40,"line":41},[38,10259,10260],{},"\u003Cinput \n",[38,10262,10263],{"class":40,"line":47},[38,10264,10265],{},"  v-if=\"fieldRule.limitType === 'maxlength'\" \n",[38,10267,10268],{"class":40,"line":62},[38,10269,10270],{},"  :maxlength=\"fieldRule.limitValue\" \n",[38,10272,10273],{"class":40,"line":173},[38,10274,10275],{},"\u002F>\n",[38,10277,10278],{"class":40,"line":179},[38,10279,10260],{},[38,10281,10282],{"class":40,"line":185},[38,10283,10284],{},"  v-if=\"fieldRule.limitType === 'max'\" \n",[38,10286,10287],{"class":40,"line":191},[38,10288,10289],{},"  :max=\"fieldRule.limitValue\" \n",[38,10291,10292],{"class":40,"line":197},[38,10293,10275],{},[608,10295,10162],{"id":10296},"使用動態參數-1",[28,10298,10300],{"className":9361,"code":10299,"language":9363,"meta":33,"style":33},"\u003Cinput :[fieldRule.limitType]=\"fieldRule.limitValue\" \u002F>\n",[35,10301,10302],{"__ignoreMap":33},[38,10303,10304],{"class":40,"line":41},[38,10305,10299],{},[56,10307,58],{},{"title":33,"searchDepth":47,"depth":47,"links":10309},[10310,10314,10315,10318,10322],{"id":9740,"depth":47,"text":9741,"children":10311},[10312,10313],{"id":9744,"depth":62,"text":9745},{"id":9758,"depth":62,"text":9759},{"id":9771,"depth":47,"text":9772},{"id":9786,"depth":47,"text":9787,"children":10316},[10317],{"id":9790,"depth":62,"text":9791},{"id":9820,"depth":47,"text":9821,"children":10319},[10320,10321],{"id":9838,"depth":62,"text":9838},{"id":9886,"depth":62,"text":9887},{"id":9939,"depth":47,"text":9940,"children":10323},[10324,10325,10326,10327],{"id":9948,"depth":62,"text":9949},{"id":10045,"depth":62,"text":10046},{"id":10101,"depth":62,"text":10102},{"id":10203,"depth":62,"text":10204},"Vue 在建置階段（或執行初期），會在底層把你寫的 \u003Ctemplate> 全部轉換成高效率的 JavaScript 渲染函式 (Render Function)",{},"\u002Fnotes\u002Fsync\u002Fvue-02-template-syntax",{"title":9735,"description":10328},"Vue\u002F02-Template Syntax.md","notes\u002Fsync\u002Fvue-02-template-syntax",[2649],"iGRmf4BFEQ_HwfCBPLDs8oiPyji4fyKu8_wVbzg1a98",{"id":10337,"title":10338,"body":10339,"category":2640,"date":9332,"description":10350,"draft":67,"extension":68,"meta":10601,"navigation":70,"path":10602,"seo":10603,"sourcePath":10604,"stem":10605,"tags":10606,"__hash__":10607},"notes\u002Fnotes\u002Fsync\u002Fvue-03-reactivity-fundamentals.md","Reactivity Fundamentals",{"type":8,"value":10340,"toc":10596},[10341,10345,10348,10351,10370,10373,10410,10413,10485,10491,10520,10523,10526,10590,10594],[85,10342,10344],{"id":10343},"declaring-reactive-state","Declaring Reactive State",[20,10346,10347],{"id":2566},"ref()",[11,10349,10350],{},"宣告響應式狀態的建議方法",[28,10352,10354],{"className":9361,"code":10353,"language":9363,"meta":33,"style":33},"import { ref } from 'vue'\n\nconst count = ref(0)\n",[35,10355,10356,10361,10365],{"__ignoreMap":33},[38,10357,10358],{"class":40,"line":41},[38,10359,10360],{},"import { ref } from 'vue'\n",[38,10362,10363],{"class":40,"line":47},[38,10364,194],{"emptyLinePlaceholder":70},[38,10366,10367],{"class":40,"line":62},[38,10368,10369],{},"const count = ref(0)\n",[11,10371,10372],{},"ref() 函數接受一個參數，並將其包裝在一個帶有 .value 屬性的 ref 物件中傳回",[28,10374,10376],{"className":9361,"code":10375,"language":9363,"meta":33,"style":33},"const count = ref(0)\n\nconsole.log(count) \u002F\u002F { value: 0 }\nconsole.log(count.value) \u002F\u002F 0\n\ncount.value++\nconsole.log(count.value) \u002F\u002F 1\n",[35,10377,10378,10382,10386,10391,10396,10400,10405],{"__ignoreMap":33},[38,10379,10380],{"class":40,"line":41},[38,10381,10369],{},[38,10383,10384],{"class":40,"line":47},[38,10385,194],{"emptyLinePlaceholder":70},[38,10387,10388],{"class":40,"line":62},[38,10389,10390],{},"console.log(count) \u002F\u002F { value: 0 }\n",[38,10392,10393],{"class":40,"line":173},[38,10394,10395],{},"console.log(count.value) \u002F\u002F 0\n",[38,10397,10398],{"class":40,"line":179},[38,10399,194],{"emptyLinePlaceholder":70},[38,10401,10402],{"class":40,"line":185},[38,10403,10404],{},"count.value++\n",[38,10406,10407],{"class":40,"line":191},[38,10408,10409],{},"console.log(count.value) \u002F\u002F 1\n",[11,10411,10412],{},"setup() 函數中宣告並傳回它們",[28,10414,10416],{"className":9361,"code":10415,"language":9363,"meta":33,"style":33},"\u003Cscript setup>\nimport { ref } from 'vue'\n\nconst count = ref(0)\n\nfunction increment() {\n  count.value++\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cbutton @click=\"increment\">\n    {{ count }}\n  \u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n",[35,10417,10418,10423,10427,10431,10435,10439,10444,10449,10453,10458,10462,10466,10471,10476,10481],{"__ignoreMap":33},[38,10419,10420],{"class":40,"line":41},[38,10421,10422],{},"\u003Cscript setup>\n",[38,10424,10425],{"class":40,"line":47},[38,10426,10360],{},[38,10428,10429],{"class":40,"line":62},[38,10430,194],{"emptyLinePlaceholder":70},[38,10432,10433],{"class":40,"line":173},[38,10434,10369],{},[38,10436,10437],{"class":40,"line":179},[38,10438,194],{"emptyLinePlaceholder":70},[38,10440,10441],{"class":40,"line":185},[38,10442,10443],{},"function increment() {\n",[38,10445,10446],{"class":40,"line":191},[38,10447,10448],{},"  count.value++\n",[38,10450,10451],{"class":40,"line":197},[38,10452,270],{},[38,10454,10455],{"class":40,"line":203},[38,10456,10457],{},"\u003C\u002Fscript>\n",[38,10459,10460],{"class":40,"line":209},[38,10461,194],{"emptyLinePlaceholder":70},[38,10463,10464],{"class":40,"line":215},[38,10465,9496],{},[38,10467,10468],{"class":40,"line":221},[38,10469,10470],{},"  \u003Cbutton @click=\"increment\">\n",[38,10472,10473],{"class":40,"line":227},[38,10474,10475],{},"    {{ count }}\n",[38,10477,10478],{"class":40,"line":232},[38,10479,10480],{},"  \u003C\u002Fbutton>\n",[38,10482,10483],{"class":40,"line":238},[38,10484,9511],{},[608,10486,10488,10489],{"id":10487},"為什麼是ref","為什麼是",[35,10490,10347],{},[122,10492,10493,10502,10511],{},[125,10494,10495,10498,10499,10501],{},[15,10496,10497],{},"自動追蹤與更新","：首次渲染時追蹤 ",[35,10500,2566],{},"，數值改變即自動更新 DOM。",[125,10503,10504,10510],{},[15,10505,10506,10509],{},[35,10507,10508],{},"value"," 的作用","：讓 Vue 能夠攔截並偵測數值的存取與修改。",[125,10512,10513,10516,10517,10519],{},[15,10514,10515],{},"保持響應性傳遞","：可將 ",[35,10518,2566],{}," 傳入函式且不失聯，適合抽離與重構邏輯。",[7382,10521,10522],{"id":2051},"補充：",[11,10524,10525],{},"在底層，Vue 在 getter 中執行跟踪，並在 setter 中執行觸發。從概念上講，你可以將 ref 想像成一個類似這樣的物件：",[28,10527,10529],{"className":9361,"code":10528,"language":9363,"meta":33,"style":33},"\u002F\u002F pseudo code, not actual implementation\nconst myRef = {\n  _value: 0,\n  get value() {\n    track()\n    return this._value\n  },\n  set value(newValue) {\n    this._value = newValue\n    trigger()\n  }\n}\n",[35,10530,10531,10536,10541,10546,10551,10556,10561,10566,10571,10576,10581,10586],{"__ignoreMap":33},[38,10532,10533],{"class":40,"line":41},[38,10534,10535],{},"\u002F\u002F pseudo code, not actual implementation\n",[38,10537,10538],{"class":40,"line":47},[38,10539,10540],{},"const myRef = {\n",[38,10542,10543],{"class":40,"line":62},[38,10544,10545],{},"  _value: 0,\n",[38,10547,10548],{"class":40,"line":173},[38,10549,10550],{},"  get value() {\n",[38,10552,10553],{"class":40,"line":179},[38,10554,10555],{},"    track()\n",[38,10557,10558],{"class":40,"line":185},[38,10559,10560],{},"    return this._value\n",[38,10562,10563],{"class":40,"line":191},[38,10564,10565],{},"  },\n",[38,10567,10568],{"class":40,"line":197},[38,10569,10570],{},"  set value(newValue) {\n",[38,10572,10573],{"class":40,"line":203},[38,10574,10575],{},"    this._value = newValue\n",[38,10577,10578],{"class":40,"line":209},[38,10579,10580],{},"    trigger()\n",[38,10582,10583],{"class":40,"line":215},[38,10584,10585],{},"  }\n",[38,10587,10588],{"class":40,"line":221},[38,10589,270],{},[608,10591,10593],{"id":10592},"deep-reactivity","Deep Reactivity",[56,10595,58],{},{"title":33,"searchDepth":47,"depth":47,"links":10597},[10598],{"id":10343,"depth":47,"text":10344,"children":10599},[10600],{"id":2566,"depth":62,"text":10347},{},"\u002Fnotes\u002Fsync\u002Fvue-03-reactivity-fundamentals",{"title":10338,"description":10350},"Vue\u002F03-Reactivity Fundamentals.md","notes\u002Fsync\u002Fvue-03-reactivity-fundamentals",[2649],"duR2kYCNZfv8QjPTTlA4tIr2MkGi7SdFRti6eojxMNY",{"id":10609,"title":10610,"body":10611,"category":2640,"date":9332,"description":11086,"draft":67,"extension":68,"meta":11087,"navigation":70,"path":11088,"seo":11089,"sourcePath":11090,"stem":11091,"tags":11092,"__hash__":11093},"notes\u002Fnotes\u002Fsync\u002Fvue-04-computed-properties.md","Computed Properties",{"type":8,"value":10612,"toc":11081},[10613,10719,10753,10757,10763,10768,10772,10802,10804,10808,10811,10899,10901,10905,10913,10916,10993,10996,11079],[28,10614,10616],{"className":9361,"code":10615,"language":9363,"meta":33,"style":33},"\u003Cscript setup>\nimport { reactive, computed } from 'vue'\n\nconst author = reactive({\n  name: 'John Doe',\n  books: [\n    'Vue 2 - Advanced Guide',\n    'Vue 3 - Basic Guide',\n    'Vue 4 - The Mystery'\n  ]\n})\n\n\u002F\u002F a computed ref\nconst publishedBooksMessage = computed(() => {\n  return author.books.length > 0 ? 'Yes' : 'No'\n})\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cp>Has published books:\u003C\u002Fp>\n  \u003Cspan>{{ publishedBooksMessage }}\u003C\u002Fspan>\n\u003C\u002Ftemplate>\n",[35,10617,10618,10622,10627,10631,10636,10641,10646,10651,10656,10661,10666,10670,10674,10679,10684,10689,10693,10697,10701,10705,10710,10715],{"__ignoreMap":33},[38,10619,10620],{"class":40,"line":41},[38,10621,10422],{},[38,10623,10624],{"class":40,"line":47},[38,10625,10626],{},"import { reactive, computed } from 'vue'\n",[38,10628,10629],{"class":40,"line":62},[38,10630,194],{"emptyLinePlaceholder":70},[38,10632,10633],{"class":40,"line":173},[38,10634,10635],{},"const author = reactive({\n",[38,10637,10638],{"class":40,"line":179},[38,10639,10640],{},"  name: 'John Doe',\n",[38,10642,10643],{"class":40,"line":185},[38,10644,10645],{},"  books: [\n",[38,10647,10648],{"class":40,"line":191},[38,10649,10650],{},"    'Vue 2 - Advanced Guide',\n",[38,10652,10653],{"class":40,"line":197},[38,10654,10655],{},"    'Vue 3 - Basic Guide',\n",[38,10657,10658],{"class":40,"line":203},[38,10659,10660],{},"    'Vue 4 - The Mystery'\n",[38,10662,10663],{"class":40,"line":209},[38,10664,10665],{},"  ]\n",[38,10667,10668],{"class":40,"line":215},[38,10669,9389],{},[38,10671,10672],{"class":40,"line":221},[38,10673,194],{"emptyLinePlaceholder":70},[38,10675,10676],{"class":40,"line":227},[38,10677,10678],{},"\u002F\u002F a computed ref\n",[38,10680,10681],{"class":40,"line":232},[38,10682,10683],{},"const publishedBooksMessage = computed(() => {\n",[38,10685,10686],{"class":40,"line":238},[38,10687,10688],{},"  return author.books.length > 0 ? 'Yes' : 'No'\n",[38,10690,10691],{"class":40,"line":244},[38,10692,9389],{},[38,10694,10695],{"class":40,"line":249},[38,10696,10457],{},[38,10698,10699],{"class":40,"line":255},[38,10700,194],{"emptyLinePlaceholder":70},[38,10702,10703],{"class":40,"line":261},[38,10704,9496],{},[38,10706,10707],{"class":40,"line":267},[38,10708,10709],{},"  \u003Cp>Has published books:\u003C\u002Fp>\n",[38,10711,10712],{"class":40,"line":1264},[38,10713,10714],{},"  \u003Cspan>{{ publishedBooksMessage }}\u003C\u002Fspan>\n",[38,10716,10717],{"class":40,"line":1269},[38,10718,9511],{},[122,10720,10721],{},[125,10722,10723,10724,10727,10728],{},"為什麼要用 ",[35,10725,10726],{},"computed"," 而不是普通函式？\n",[122,10729,10730,10740],{},[125,10731,10732,10735,10736,10739],{},[15,10733,10734],{},"自動追蹤依賴："," 你不需要手動告訴 Vue「當 ",[35,10737,10738],{},"author.books"," 變動時要重新計算」，Vue 自己會知道。",[125,10741,10742,10745,10746,10748,10749,10752],{},[15,10743,10744],{},"快取（Cache）機制："," 只要 ",[35,10747,10738],{}," 沒有改變，不管畫面重新渲染多少次，",[35,10750,10751],{},"publishedBooksMessage"," 都會直接回傳上一次算好的舊結果，不會浪費效能重新計算。",[85,10754,10756],{"id":10755},"computed-caching-vs-methods","Computed Caching vs. Methods",[11,10758,10759,10760,10762],{},"既然寫成普通函式（Method）也可以在畫面上印出一樣的結果，為什麼還要多一個 ",[35,10761,10726],{},"（計算屬性）？",[89,10764,10765],{},[11,10766,10767],{},"快取機制",[608,10769,10771],{"id":10770},"補充angular的實現方法跟改革","補充：Angular的實現方法跟改革",[122,10773,10774,10799],{},[125,10775,10776,10777,10780,10781],{},"在Angular當中舊版的機制是用zone.js來處理 -> 攔截所有非步事件（如 ",[35,10778,10779],{},"setTimeout","、HTTP 請求、點擊事件），但會導致兩個問題：",[122,10782,10783,10793],{},[125,10784,10785,10788,10789,10792],{},[15,10786,10787],{},"沒有精準追蹤","：Zone.js 其實",[15,10790,10791],{},"不知道","到底是哪一個變數改了，只知道「有事情發生」，所以會進行髒檢查（Dirty Checking）。",[125,10794,10795,10798],{},[15,10796,10797],{},"效能負擔","：當專案變大時，容易造成不必要的全局過濾與重新渲染。",[125,10800,10801],{},"新版的Angular引入Signals的概念",[272,10803],{},[85,10805,10807],{"id":10806},"writable-computed","Writable Computed",[11,10809,10810],{},"計算屬性預設僅支援 getter 方法。如果您嘗試為計算屬性賦值，將會收到執行時間警告。可以透過同時提供 getter 和 setter 方法來建立一個：",[28,10812,10814],{"className":9361,"code":10813,"language":9363,"meta":33,"style":33},"\u003Cscript setup>\nimport { ref, computed } from 'vue'\n\nconst firstName = ref('John')\nconst lastName = ref('Doe')\n\nconst fullName = computed({\n  \u002F\u002F getter\n  get() {\n    return firstName.value + ' ' + lastName.value\n  },\n  \u002F\u002F setter\n  set(newValue) {\n    \u002F\u002F Note: we are using destructuring assignment syntax here.\n    [firstName.value, lastName.value] = newValue.split(' ')\n  }\n})\n\u003C\u002Fscript>\n",[35,10815,10816,10820,10825,10829,10834,10839,10843,10848,10853,10858,10863,10867,10872,10877,10882,10887,10891,10895],{"__ignoreMap":33},[38,10817,10818],{"class":40,"line":41},[38,10819,10422],{},[38,10821,10822],{"class":40,"line":47},[38,10823,10824],{},"import { ref, computed } from 'vue'\n",[38,10826,10827],{"class":40,"line":62},[38,10828,194],{"emptyLinePlaceholder":70},[38,10830,10831],{"class":40,"line":173},[38,10832,10833],{},"const firstName = ref('John')\n",[38,10835,10836],{"class":40,"line":179},[38,10837,10838],{},"const lastName = ref('Doe')\n",[38,10840,10841],{"class":40,"line":185},[38,10842,194],{"emptyLinePlaceholder":70},[38,10844,10845],{"class":40,"line":191},[38,10846,10847],{},"const fullName = computed({\n",[38,10849,10850],{"class":40,"line":197},[38,10851,10852],{},"  \u002F\u002F getter\n",[38,10854,10855],{"class":40,"line":203},[38,10856,10857],{},"  get() {\n",[38,10859,10860],{"class":40,"line":209},[38,10861,10862],{},"    return firstName.value + ' ' + lastName.value\n",[38,10864,10865],{"class":40,"line":215},[38,10866,10565],{},[38,10868,10869],{"class":40,"line":221},[38,10870,10871],{},"  \u002F\u002F setter\n",[38,10873,10874],{"class":40,"line":227},[38,10875,10876],{},"  set(newValue) {\n",[38,10878,10879],{"class":40,"line":232},[38,10880,10881],{},"    \u002F\u002F Note: we are using destructuring assignment syntax here.\n",[38,10883,10884],{"class":40,"line":238},[38,10885,10886],{},"    [firstName.value, lastName.value] = newValue.split(' ')\n",[38,10888,10889],{"class":40,"line":244},[38,10890,10585],{},[38,10892,10893],{"class":40,"line":249},[38,10894,9389],{},[38,10896,10897],{"class":40,"line":255},[38,10898,10457],{},[272,10900],{},[85,10902,10904],{"id":10903},"getting-the-previous-value","Getting the Previous Value",[89,10906,10907],{},[11,10908,10909,10910],{},"可以取得上一次計算後回傳的結果，並非一定是",[15,10911,10912],{},"初始值",[11,10914,10915],{},"可以取得先前的值：",[28,10917,10919],{"className":9361,"code":10918,"language":9363,"meta":33,"style":33},"\u003Cscript setup>\nimport { ref, computed } from 'vue'\n\nconst count = ref(2)\n\n\u002F\u002F This computed will return the value of count when it's less or equal to 3.\n\u002F\u002F When count is >=4, the last value that fulfilled our condition will be returned\n\u002F\u002F instead until count is less or equal to 3\nconst alwaysSmall = computed((previous) => {\n  if (count.value \u003C= 3) {\n    return count.value\n  }\n\n  return previous\n})\n\u003C\u002Fscript>\n",[35,10920,10921,10925,10929,10933,10938,10942,10947,10952,10957,10962,10967,10972,10976,10980,10985,10989],{"__ignoreMap":33},[38,10922,10923],{"class":40,"line":41},[38,10924,10422],{},[38,10926,10927],{"class":40,"line":47},[38,10928,10824],{},[38,10930,10931],{"class":40,"line":62},[38,10932,194],{"emptyLinePlaceholder":70},[38,10934,10935],{"class":40,"line":173},[38,10936,10937],{},"const count = ref(2)\n",[38,10939,10940],{"class":40,"line":179},[38,10941,194],{"emptyLinePlaceholder":70},[38,10943,10944],{"class":40,"line":185},[38,10945,10946],{},"\u002F\u002F This computed will return the value of count when it's less or equal to 3.\n",[38,10948,10949],{"class":40,"line":191},[38,10950,10951],{},"\u002F\u002F When count is >=4, the last value that fulfilled our condition will be returned\n",[38,10953,10954],{"class":40,"line":197},[38,10955,10956],{},"\u002F\u002F instead until count is less or equal to 3\n",[38,10958,10959],{"class":40,"line":203},[38,10960,10961],{},"const alwaysSmall = computed((previous) => {\n",[38,10963,10964],{"class":40,"line":209},[38,10965,10966],{},"  if (count.value \u003C= 3) {\n",[38,10968,10969],{"class":40,"line":215},[38,10970,10971],{},"    return count.value\n",[38,10973,10974],{"class":40,"line":221},[38,10975,10585],{},[38,10977,10978],{"class":40,"line":227},[38,10979,194],{"emptyLinePlaceholder":70},[38,10981,10982],{"class":40,"line":232},[38,10983,10984],{},"  return previous\n",[38,10986,10987],{"class":40,"line":238},[38,10988,9389],{},[38,10990,10991],{"class":40,"line":244},[38,10992,10457],{},[11,10994,10995],{},"搭配可寫入值：",[28,10997,10999],{"className":9361,"code":10998,"language":9363,"meta":33,"style":33},"\u003Cscript setup>\nimport { ref, computed } from 'vue'\n\nconst count = ref(2)\n\nconst alwaysSmall = computed({\n  get(previous) {\n    if (count.value \u003C= 3) {\n      return count.value\n    }\n\n    return previous\n  },\n  set(newValue) {\n    count.value = newValue * 2\n  }\n})\n\u003C\u002Fscript>\n",[35,11000,11001,11005,11009,11013,11017,11021,11026,11031,11036,11041,11045,11049,11054,11058,11062,11067,11071,11075],{"__ignoreMap":33},[38,11002,11003],{"class":40,"line":41},[38,11004,10422],{},[38,11006,11007],{"class":40,"line":47},[38,11008,10824],{},[38,11010,11011],{"class":40,"line":62},[38,11012,194],{"emptyLinePlaceholder":70},[38,11014,11015],{"class":40,"line":173},[38,11016,10937],{},[38,11018,11019],{"class":40,"line":179},[38,11020,194],{"emptyLinePlaceholder":70},[38,11022,11023],{"class":40,"line":185},[38,11024,11025],{},"const alwaysSmall = computed({\n",[38,11027,11028],{"class":40,"line":191},[38,11029,11030],{},"  get(previous) {\n",[38,11032,11033],{"class":40,"line":197},[38,11034,11035],{},"    if (count.value \u003C= 3) {\n",[38,11037,11038],{"class":40,"line":203},[38,11039,11040],{},"      return count.value\n",[38,11042,11043],{"class":40,"line":209},[38,11044,264],{},[38,11046,11047],{"class":40,"line":215},[38,11048,194],{"emptyLinePlaceholder":70},[38,11050,11051],{"class":40,"line":221},[38,11052,11053],{},"    return previous\n",[38,11055,11056],{"class":40,"line":227},[38,11057,10565],{},[38,11059,11060],{"class":40,"line":232},[38,11061,10876],{},[38,11063,11064],{"class":40,"line":238},[38,11065,11066],{},"    count.value = newValue * 2\n",[38,11068,11069],{"class":40,"line":244},[38,11070,10585],{},[38,11072,11073],{"class":40,"line":249},[38,11074,9389],{},[38,11076,11077],{"class":40,"line":255},[38,11078,10457],{},[56,11080,58],{},{"title":33,"searchDepth":47,"depth":47,"links":11082},[11083,11084,11085],{"id":10755,"depth":47,"text":10756},{"id":10806,"depth":47,"text":10807},{"id":10903,"depth":47,"text":10904},"\u003Cscript setup>",{},"\u002Fnotes\u002Fsync\u002Fvue-04-computed-properties",{"title":10610,"description":11086},"Vue\u002F04-Computed Properties.md","notes\u002Fsync\u002Fvue-04-computed-properties",[2649],"gArBt7_1DSvhG0u4ZeMNeAfWxUF1ZjTWOUSiZmxUUZ4",{"id":11095,"title":11096,"body":11097,"category":2640,"date":9332,"description":11240,"draft":67,"extension":68,"meta":11241,"navigation":70,"path":11242,"seo":11243,"sourcePath":11244,"stem":11245,"tags":11246,"__hash__":11247},"notes\u002Fnotes\u002Fsync\u002Fvue-05-class-and-style-bindings.md","Class and Style Bindings",{"type":8,"value":11098,"toc":11235},[11099,11107,11111,11115,11124,11133,11143,11180,11188,11212,11221,11224,11233],[11,11100,11101,11102,10064,11104,11106],{},"在 Vue 中給元素綁定 ",[35,11103,2065],{},[35,11105,56],{}," 時，不用再辛苦地自己「拼字串」，Vue 提供了可以直接傳入「物件（Object）」或「陣列（Array）」的快捷寫法。",[85,11108,11110],{"id":11109},"binding-html-classes","Binding HTML Classes",[20,11112,11114],{"id":11113},"binding-to-objects","Binding to Objects",[122,11116,11117],{},[125,11118,11119,11120,11123],{},"可以透過動態樣式",[35,11121,11122],{},":class","來傳遞",[28,11125,11127],{"className":9361,"code":11126,"language":9363,"meta":33,"style":33},"\u003Cdiv :class=\"{ active: isActive }\">\u003C\u002Fdiv>\n",[35,11128,11129],{"__ignoreMap":33},[38,11130,11131],{"class":40,"line":41},[38,11132,11126],{},[122,11134,11135],{},[125,11136,11137,11139,11140,11142],{},[35,11138,11122],{}," 指令也可以與普通的 ",[35,11141,2065],{}," 屬性共存。例如，在以下狀態：",[28,11144,11146],{"className":9361,"code":11145,"language":9363,"meta":33,"style":33},"const isActive = ref(true) \nconst hasError = ref(false)\n\n\u003Cdiv \n    class=\"static\" \n    :class=\"{ active: isActive, 'text-danger': hasError }\" >\n\u003C\u002Fdiv>\n\n",[35,11147,11148,11153,11158,11162,11166,11171,11176],{"__ignoreMap":33},[38,11149,11150],{"class":40,"line":41},[38,11151,11152],{},"const isActive = ref(true) \n",[38,11154,11155],{"class":40,"line":47},[38,11156,11157],{},"const hasError = ref(false)\n",[38,11159,11160],{"class":40,"line":62},[38,11161,194],{"emptyLinePlaceholder":70},[38,11163,11164],{"class":40,"line":173},[38,11165,10135],{},[38,11167,11168],{"class":40,"line":179},[38,11169,11170],{},"    class=\"static\" \n",[38,11172,11173],{"class":40,"line":185},[38,11174,11175],{},"    :class=\"{ active: isActive, 'text-danger': hasError }\" >\n",[38,11177,11178],{"class":40,"line":191},[38,11179,9541],{},[122,11181,11182],{},[125,11183,11184,11185,7660],{},"不需要把全部的 Class 物件都硬塞（Inline）在 HTML 模板裡，可以直接把整個物件抽出來寫在 ",[35,11186,11187],{},"\u003Cscript>",[28,11189,11191],{"className":9361,"code":11190,"language":9363,"meta":33,"style":33},"const classObject = reactive({\n  active: true,\n  'text-danger': false\n})\n",[35,11192,11193,11198,11203,11208],{"__ignoreMap":33},[38,11194,11195],{"class":40,"line":41},[38,11196,11197],{},"const classObject = reactive({\n",[38,11199,11200],{"class":40,"line":47},[38,11201,11202],{},"  active: true,\n",[38,11204,11205],{"class":40,"line":62},[38,11206,11207],{},"  'text-danger': false\n",[38,11209,11210],{"class":40,"line":173},[38,11211,9389],{},[28,11213,11215],{"className":9361,"code":11214,"language":9363,"meta":33,"style":33},"\u003Cdiv :class=\"classObject\">\u003C\u002Fdiv>\n",[35,11216,11217],{"__ignoreMap":33},[38,11218,11219],{"class":40,"line":41},[38,11220,11214],{},[11,11222,11223],{},"會渲染成：",[28,11225,11227],{"className":9361,"code":11226,"language":9363,"meta":33,"style":33},"\u003Cdiv class=\"active\">\u003C\u002Fdiv>\n",[35,11228,11229],{"__ignoreMap":33},[38,11230,11231],{"class":40,"line":41},[38,11232,11226],{},[56,11234,58],{},{"title":33,"searchDepth":47,"depth":47,"links":11236},[11237],{"id":11109,"depth":47,"text":11110,"children":11238},[11239],{"id":11113,"depth":62,"text":11114},"在 Vue 中給元素綁定 class 或 style 時，不用再辛苦地自己「拼字串」，Vue 提供了可以直接傳入「物件（Object）」或「陣列（Array）」的快捷寫法。",{},"\u002Fnotes\u002Fsync\u002Fvue-05-class-and-style-bindings",{"title":11096,"description":11240},"Vue\u002F05-Class and Style Bindings.md","notes\u002Fsync\u002Fvue-05-class-and-style-bindings",[2649],"VhWQkErVUvw9h3yjRWEmW85hBpu6PSNeNeMot0Ns1LA",{"id":11249,"title":11250,"body":11251,"category":2640,"date":9332,"description":11704,"draft":67,"extension":68,"meta":11705,"navigation":70,"path":11706,"seo":11707,"sourcePath":11708,"stem":11709,"tags":11710,"__hash__":11711},"notes\u002Fnotes\u002Fsync\u002Fvue-實務開發.md","實務開發",{"type":8,"value":11252,"toc":11699},[11253,11256,11273,11448,11491,11495,11499,11508,11526,11530,11549,11552,11556,11569,11577,11584,11593,11596,11603,11618,11621,11636,11639,11645,11650,11654,11663,11671,11678,11687,11691,11697],[85,11254,11255],{"id":11255},"vue的渲染順序",[11,11257,11258,11259,11262,11263,11265,11266,11268,11269,11272],{},"在一個標準的",[35,11260,11261],{},".vue","檔案中，通常會有三個重要的標籤",[35,11264,9751],{}," 、",[35,11267,11187],{}," 跟 ",[35,11270,11271],{},"\u003Cstyle>","。那這三者是如何渲染的？",[28,11274,11276],{"className":9361,"code":11275,"language":9363,"meta":33,"style":33},"\n\u003Cscript setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\n\nconsole.log('1. script setup 開始')\n\nconst message = 'hello'  \nconst count = ref(0)\n\nconst double = computed(() => {  \nconsole.log('computed double 計算')  \nreturn count.value * 2  \n})\n\nwatch(count, () => {  \nconsole.log('count changed')  \n})\n\nfunction onClick() {  \nconsole.log('使用者點擊後才執行 onClick')  \ncount.value++  \n}\n\nconsole.log('2. script setup 結束')  \n\u003C\u002Fscript>\n\n\u003Ctemplate> \n    \u003Cdiv> \n        \u003Cp>{{ message }}\u003C\u002Fp> \u003Cp>{{ count }}\u003C\u002Fp>\n        \u003Cp>{{ double }}\u003C\u002Fp>\n        \u003Cbutton @click=\"onClick\">click\u003C\u002Fbutton>\n    \u003C\u002Fdiv> \n\u003C\u002Ftemplate>\n\n\u003Cstyle lang=\"scss\" scoped> \n    \u002F** style *\u002F \n\u003C\u002Fstyle>\n",[35,11277,11278,11282,11287,11292,11296,11301,11305,11310,11314,11318,11323,11328,11333,11337,11341,11346,11351,11355,11359,11364,11369,11374,11378,11382,11387,11391,11395,11400,11405,11410,11415,11420,11425,11429,11433,11438,11443],{"__ignoreMap":33},[38,11279,11280],{"class":40,"line":41},[38,11281,194],{"emptyLinePlaceholder":70},[38,11283,11284],{"class":40,"line":47},[38,11285,11286],{},"\u003Cscript setup lang=\"ts\">\n",[38,11288,11289],{"class":40,"line":62},[38,11290,11291],{},"import { computed, ref, watch } from 'vue'\n",[38,11293,11294],{"class":40,"line":173},[38,11295,194],{"emptyLinePlaceholder":70},[38,11297,11298],{"class":40,"line":179},[38,11299,11300],{},"console.log('1. script setup 開始')\n",[38,11302,11303],{"class":40,"line":185},[38,11304,194],{"emptyLinePlaceholder":70},[38,11306,11307],{"class":40,"line":191},[38,11308,11309],{},"const message = 'hello'  \n",[38,11311,11312],{"class":40,"line":197},[38,11313,10369],{},[38,11315,11316],{"class":40,"line":203},[38,11317,194],{"emptyLinePlaceholder":70},[38,11319,11320],{"class":40,"line":209},[38,11321,11322],{},"const double = computed(() => {  \n",[38,11324,11325],{"class":40,"line":215},[38,11326,11327],{},"console.log('computed double 計算')  \n",[38,11329,11330],{"class":40,"line":221},[38,11331,11332],{},"return count.value * 2  \n",[38,11334,11335],{"class":40,"line":227},[38,11336,9389],{},[38,11338,11339],{"class":40,"line":232},[38,11340,194],{"emptyLinePlaceholder":70},[38,11342,11343],{"class":40,"line":238},[38,11344,11345],{},"watch(count, () => {  \n",[38,11347,11348],{"class":40,"line":244},[38,11349,11350],{},"console.log('count changed')  \n",[38,11352,11353],{"class":40,"line":249},[38,11354,9389],{},[38,11356,11357],{"class":40,"line":255},[38,11358,194],{"emptyLinePlaceholder":70},[38,11360,11361],{"class":40,"line":261},[38,11362,11363],{},"function onClick() {  \n",[38,11365,11366],{"class":40,"line":267},[38,11367,11368],{},"console.log('使用者點擊後才執行 onClick')  \n",[38,11370,11371],{"class":40,"line":1264},[38,11372,11373],{},"count.value++  \n",[38,11375,11376],{"class":40,"line":1269},[38,11377,270],{},[38,11379,11380],{"class":40,"line":3116},[38,11381,194],{"emptyLinePlaceholder":70},[38,11383,11384],{"class":40,"line":3121},[38,11385,11386],{},"console.log('2. script setup 結束')  \n",[38,11388,11389],{"class":40,"line":3126},[38,11390,10457],{},[38,11392,11393],{"class":40,"line":3132},[38,11394,194],{"emptyLinePlaceholder":70},[38,11396,11397],{"class":40,"line":3138},[38,11398,11399],{},"\u003Ctemplate> \n",[38,11401,11402],{"class":40,"line":3143},[38,11403,11404],{},"    \u003Cdiv> \n",[38,11406,11407],{"class":40,"line":3149},[38,11408,11409],{},"        \u003Cp>{{ message }}\u003C\u002Fp> \u003Cp>{{ count }}\u003C\u002Fp>\n",[38,11411,11412],{"class":40,"line":3155},[38,11413,11414],{},"        \u003Cp>{{ double }}\u003C\u002Fp>\n",[38,11416,11417],{"class":40,"line":3161},[38,11418,11419],{},"        \u003Cbutton @click=\"onClick\">click\u003C\u002Fbutton>\n",[38,11421,11422],{"class":40,"line":3167},[38,11423,11424],{},"    \u003C\u002Fdiv> \n",[38,11426,11427],{"class":40,"line":3172},[38,11428,9511],{},[38,11430,11431],{"class":40,"line":3177},[38,11432,194],{"emptyLinePlaceholder":70},[38,11434,11435],{"class":40,"line":3183},[38,11436,11437],{},"\u003Cstyle lang=\"scss\" scoped> \n",[38,11439,11440],{"class":40,"line":3189},[38,11441,11442],{},"    \u002F** style *\u002F \n",[38,11444,11445],{"class":40,"line":3195},[38,11446,11447],{},"\u003C\u002Fstyle>\n",[122,11449,11450,11455],{},[125,11451,11452,11454],{},[35,11453,11086],{}," 會早於 template render 執行，不是建立template後才將資料塞進去",[125,11456,11457,11458],{},"整個流程會像是：",[387,11459,11460,11465,11474,11479,11482,11485,11488],{},[125,11461,11462,11464],{},[35,11463,11261],{}," 建立 component instance",[125,11466,11467,11468,11471,11472],{},"執行 ",[35,11469,11470],{},"setup()","，也就是 ",[35,11473,11086],{},[125,11475,11476,11478],{},[35,11477,11470],{}," 回傳或暴露 template 可用的資料、函式",[125,11480,11481],{},"執行 render function，也就是由 template 編譯出來的函式",[125,11483,11484],{},"render function 讀取 message \u002F count \u002F onClick",[125,11486,11487],{},"產生 Virtual DOM",[125,11489,11490],{},"Virtual DOM patch 到真實 DOM",[20,11492,11494],{"id":11493},"其他會影響渲染的","其他會影響渲染的：",[608,11496,11498],{"id":11497},"_1-computed-會影響-render但它是-lazy-的","1. computed 會影響 render，但它是 lazy 的",[28,11500,11502],{"className":9361,"code":11501,"language":9363,"meta":33,"style":33},"const double = computed(() => count.value * 2)\n",[35,11503,11504],{"__ignoreMap":33},[38,11505,11506],{"class":40,"line":41},[38,11507,11501],{},[11,11509,11510,11511,11514,11515,11518,11521,11522,11525],{},"只有 template 或其他地方讀到 ",[35,11512,11513],{},"double.value"," 時，它才會計算。",[11516,11517],"br",{},[35,11519,11520],{},"count"," 改變後，",[35,11523,11524],{},"double"," 會被標記為需要更新，下一次 render 讀取時才重新計算。",[608,11527,11529],{"id":11528},"_2-watch-watcheffect-可以在資料變動後執行副作用","2. watch \u002F watchEffect 可以在資料變動後執行副作用",[28,11531,11533],{"className":9361,"code":11532,"language":9363,"meta":33,"style":33},"watch(count, () => {\n  console.log('count changed')\n})\n",[35,11534,11535,11540,11545],{"__ignoreMap":33},[38,11536,11537],{"class":40,"line":41},[38,11538,11539],{},"watch(count, () => {\n",[38,11541,11542],{"class":40,"line":47},[38,11543,11544],{},"  console.log('count changed')\n",[38,11546,11547],{"class":40,"line":62},[38,11548,9389],{},[11,11550,11551],{},"這種不直接產生畫面，但可能會改其他 state，進而造成下一次 render。",[608,11553,11555],{"id":11554},"_3-async-api-會造成至少兩次畫面狀態","3. async API 會造成至少兩次畫面狀態",[11,11557,11558,11559,642,11562,642,11565,11568],{},"像 ",[35,11560,11561],{},"useQuery()",[35,11563,11564],{},"useAsyncData()",[35,11566,11567],{},"useFetch()"," 這類查詢通常是：",[28,11570,11575],{"className":11571,"code":11573,"language":11574},[11572],"language-text","執行 \u003Cscript setup>\n↓\n建立 query \u002F async data 狀態\n↓\n第一次 render：API 尚未回來，data 還是初始值\n↓\n畫面先使用初始狀態，例如 list = []、total = 0\n↓\nAPI 回來\n↓\ndata.value 改變\n↓\n依賴 data 的 computed，例如 list \u002F total，被標記為需要更新\n↓\n使用到 list \u002F total 的 render 範圍重新執行\n↓\n產生新的 Virtual DOM\n↓\nVue diff 新舊 Virtual DOM\n↓\n只 patch 真實 DOM 中有差異的部分\n\n","text",[35,11576,11573],{"__ignoreMap":33},[11,11578,11579,11580,11583],{},"所以在 ",[35,11581,11582],{},"vendor-payment-list"," 裡：",[28,11585,11587],{"className":9361,"code":11586,"language":9363,"meta":33,"style":33},"const { page, pageSize, list, total, applyFilters, clearFilters } = useVendorPaymentList()\n",[35,11588,11589],{"__ignoreMap":33},[38,11590,11591],{"class":40,"line":41},[38,11592,11586],{},[11,11594,11595],{},"這行會先建立列表查詢。",[11,11597,11598,11599,11602],{},"第一次 render 時，API 可能還沒有回來，因此 ",[35,11600,11601],{},"useListQuery"," 裡的預設值會先被使用：",[28,11604,11606],{"className":9361,"code":11605,"language":9363,"meta":33,"style":33},"const list = computed(() => data.value?.data ?? [])\nconst total = computed(() => data.value?.total ?? 0)\n",[35,11607,11608,11613],{"__ignoreMap":33},[38,11609,11610],{"class":40,"line":41},[38,11611,11612],{},"const list = computed(() => data.value?.data ?? [])\n",[38,11614,11615],{"class":40,"line":47},[38,11616,11617],{},"const total = computed(() => data.value?.total ?? 0)\n",[11,11619,11620],{},"所以 table 第一次可能收到：",[28,11622,11624],{"className":9361,"code":11623,"language":9363,"meta":33,"style":33},"list = []\ntotal = 0\n",[35,11625,11626,11631],{"__ignoreMap":33},[38,11627,11628],{"class":40,"line":41},[38,11629,11630],{},"list = []\n",[38,11632,11633],{"class":40,"line":47},[38,11634,11635],{},"total = 0\n",[11,11637,11638],{},"等 API 回來後：",[28,11640,11643],{"className":11641,"code":11642,"language":11574},[11572],"data.value 更新\n↓\nlist \u002F total 重新計算\n↓\nvendor-payment-list-table 收到新的 data \u002F total\n↓\n相關 render function 重新執行\n↓\nVue 只更新真實 DOM 中有差異的 table 區域\n",[35,11644,11642],{"__ignoreMap":33},[89,11646,11647],{},[11,11648,11649],{},"要注意的是，這裡的「重新 render」不是整個頁面的 DOM 全部砍掉重建。",[608,11651,11653],{"id":11652},"_4-v-if-v-for-key-會影響-component-是否建立或重建","4. v-if \u002F v-for \u002F key 會影響 component 是否建立或重建",[28,11655,11657],{"className":9361,"code":11656,"language":9363,"meta":33,"style":33},"\u003Cchild-component v-if=\"visible\" \u002F>\n",[35,11658,11659],{"__ignoreMap":33},[38,11660,11661],{"class":40,"line":41},[38,11662,11656],{},[11,11664,11665,11668,11669,142],{},[35,11666,11667],{},"visible = false"," 時，子元件根本不會建立，也不會執行子元件的 ",[35,11670,11086],{},[11,11672,11673,11674,11677],{},"如果 ",[35,11675,11676],{},"key"," 改變：",[28,11679,11681],{"className":9361,"code":11680,"language":9363,"meta":33,"style":33},"\u003Cchild-component :key=\"id\" \u002F>\n",[35,11682,11683],{"__ignoreMap":33},[38,11684,11685],{"class":40,"line":41},[38,11686,11680],{},[608,11688,11690],{"id":11689},"_5-父子元件順序","5. 父子元件順序",[28,11692,11695],{"className":11693,"code":11694,"language":11574},[11572],"父元件 setup\n↓\n父元件 render\n↓\n遇到子元件\n↓\n子元件 setup\n↓\n子元件 render\n",[35,11696,11694],{"__ignoreMap":33},[56,11698,58],{},{"title":33,"searchDepth":47,"depth":47,"links":11700},[11701],{"id":11255,"depth":47,"text":11255,"children":11702},[11703],{"id":11493,"depth":62,"text":11494},"在一個標準的.vue檔案中，通常會有三個重要的標籤\u003Ctemplate> 、\u003Cscript> 跟 \u003Cstyle>。那這三者是如何渲染的？",{},"\u002Fnotes\u002Fsync\u002Fvue",{"title":11250,"description":11704},"Vue\u002F實務開發.md","notes\u002Fsync\u002Fvue-實務開發",[2649],"oCBttA3Pdb5jdxlGEeIzDo1eDffTimr0DFH8qpUwZYY",{"id":11713,"title":11714,"body":11715,"category":64,"date":11830,"description":11831,"draft":67,"extension":68,"meta":11832,"navigation":70,"path":11833,"seo":11834,"sourcePath":2646,"stem":11835,"tags":11836,"__hash__":11837},"notes\u002Fnotes\u002Fcsharp-linq-basics.md","C# LINQ 查詢入門",{"type":8,"value":11716,"toc":11824},[11717,11721,11724,11727,11733,11758,11764,11784,11787,11796,11799,11807,11809,11822],[85,11718,11720],{"id":11719},"linq-在做什麼","LINQ 在做什麼",[11,11722,11723],{},"LINQ 把「篩選、投影、排序」寫成可組合的查詢。同一套思維可用在記憶體集合與部分資料來源。",[85,11725,11726],{"id":11726},"兩種寫法",[11,11728,11729,11732],{},[15,11730,11731],{},"查詢語法","適合表達意圖：",[28,11734,11736],{"className":30,"code":11735,"language":32,"meta":33,"style":33},"var result =\n    from item in items\n    where item.IsActive\n    select item.Name;\n",[35,11737,11738,11743,11748,11753],{"__ignoreMap":33},[38,11739,11740],{"class":40,"line":41},[38,11741,11742],{},"var result =\n",[38,11744,11745],{"class":40,"line":47},[38,11746,11747],{},"    from item in items\n",[38,11749,11750],{"class":40,"line":62},[38,11751,11752],{},"    where item.IsActive\n",[38,11754,11755],{"class":40,"line":173},[38,11756,11757],{},"    select item.Name;\n",[11,11759,11760,11763],{},[15,11761,11762],{},"方法語法","適合串接與重用：",[28,11765,11767],{"className":30,"code":11766,"language":32,"meta":33,"style":33},"var result = items\n    .Where(item => item.IsActive)\n    .Select(item => item.Name);\n",[35,11768,11769,11774,11779],{"__ignoreMap":33},[38,11770,11771],{"class":40,"line":41},[38,11772,11773],{},"var result = items\n",[38,11775,11776],{"class":40,"line":47},[38,11777,11778],{},"    .Where(item => item.IsActive)\n",[38,11780,11781],{"class":40,"line":62},[38,11782,11783],{},"    .Select(item => item.Name);\n",[85,11785,11786],{"id":11786},"延遲執行",[11,11788,11789,11790,322,11792,11795],{},"多數 LINQ 運算子是延遲的：定義查詢時還不會跑，直到你列舉（",[35,11791,308],{},[35,11793,11794],{},"ToList","）才執行。",[11,11797,11798],{},"這代表：",[122,11800,11801,11804],{},[125,11802,11803],{},"可以先組查詢再決定要不要物化",[125,11805,11806],{},"也要小心在迴圈裡重複列舉造成重複成本",[85,11808,2621],{"id":2621},[11,11810,11811,11812,642,11815,11818,11819,11821],{},"先用 ",[35,11813,11814],{},"Where",[35,11816,11817],{},"Select"," 把資料收成你要的形狀，需要時再 ",[35,11820,11794],{},"。查詢語法與方法語法可以並存，選可讀性較高的那一種。",[56,11823,58],{},{"title":33,"searchDepth":47,"depth":47,"links":11825},[11826,11827,11828,11829],{"id":11719,"depth":47,"text":11720},{"id":11726,"depth":47,"text":11726},{"id":11786,"depth":47,"text":11786},{"id":2621,"depth":47,"text":2621},"2026-07-18","用查詢語法與方法語法對照，釐清 Where、Select 與延遲執行的基本用法。",{},"\u002Fnotes\u002Fcsharp-linq-basics",{"title":11714,"description":11831},"notes\u002Fcsharp-linq-basics",[76,77],"r3Gm7My6UeB0XayprWlDG4UUv4QeB2RNr51xOJlpoJI",{"id":11839,"title":11840,"body":11841,"category":64,"date":11830,"description":12282,"draft":67,"extension":68,"meta":12283,"navigation":70,"path":12284,"seo":12285,"sourcePath":12286,"stem":12287,"tags":12288,"__hash__":12289},"notes\u002Fnotes\u002Fsync\u002Fcsharp-c-基本型別.md","C# 基本型別",{"type":8,"value":11842,"toc":12264},[11843,11846,11850,11854,11870,11874,11879,11936,11942,11946,11951,11955,11960,11964,11985,11988,11993,11995,11998,12003,12007,12012,12021,12049,12073,12078,12080,12084,12126,12154,12199,12204,12206,12209,12213,12224,12227,12230,12233,12236,12242,12245,12248,12251,12254,12257,12260,12262],[85,11844,11845],{"id":11845},"整數和浮點數",[20,11847,11849],{"id":11848},"整數-int","整數 int",[20,11851,11853],{"id":11852},"雙精度浮點數-double","雙精度浮點數 double",[89,11855,11856],{},[11,11857,11858,11861,11862,11865,11866,11869],{},[35,11859,11860],{},"浮點數","適合表示大小可非常大或非常小的非整數\n",[35,11863,11864],{},"雙精確度"," 是一個相對詞彙，描述用來儲存值的二進位數位數。雙精確度 數位的二進位數是 單精度的兩倍。\n",[35,11867,11868],{},"單精度"," 數是透過關鍵字 float 來宣告的",[20,11871,11873],{"id":11872},"十進位類型-decimal","十進位類型 decimal",[89,11875,11876],{},[11,11877,11878],{},"decimal 類型的範圍較小，但精確度大於 double",[28,11880,11882],{"className":30,"code":11881,"language":32,"meta":33,"style":33},"double a = 1.0;\ndouble b = 3.0;\nConsole.WriteLine(a \u002F b);\n### 0.3333333333333333\n\n\n\ndecimal c = 1.0M;\ndecimal d = 3.0M;\nConsole.WriteLine(c \u002F d);\n### 0.3333333333333333333333333333\n",[35,11883,11884,11889,11894,11899,11904,11908,11912,11916,11921,11926,11931],{"__ignoreMap":33},[38,11885,11886],{"class":40,"line":41},[38,11887,11888],{},"double a = 1.0;\n",[38,11890,11891],{"class":40,"line":47},[38,11892,11893],{},"double b = 3.0;\n",[38,11895,11896],{"class":40,"line":62},[38,11897,11898],{},"Console.WriteLine(a \u002F b);\n",[38,11900,11901],{"class":40,"line":173},[38,11902,11903],{},"### 0.3333333333333333\n",[38,11905,11906],{"class":40,"line":179},[38,11907,194],{"emptyLinePlaceholder":70},[38,11909,11910],{"class":40,"line":185},[38,11911,194],{"emptyLinePlaceholder":70},[38,11913,11914],{"class":40,"line":191},[38,11915,194],{"emptyLinePlaceholder":70},[38,11917,11918],{"class":40,"line":197},[38,11919,11920],{},"decimal c = 1.0M;\n",[38,11922,11923],{"class":40,"line":203},[38,11924,11925],{},"decimal d = 3.0M;\n",[38,11927,11928],{"class":40,"line":209},[38,11929,11930],{},"Console.WriteLine(c \u002F d);\n",[38,11932,11933],{"class":40,"line":215},[38,11934,11935],{},"### 0.3333333333333333333333333333\n",[28,11937,11940],{"className":11938,"code":11939,"language":11574},[11572],"M字母是在關鍵字double和decimal之間，從視覺上看最為顯眼的字母。\n",[35,11941,11939],{"__ignoreMap":33},[20,11943,11945],{"id":11944},"短整數-short","短整數 short",[89,11947,11948],{},[11,11949,11950],{},"Stack 佔用大小：2 位元組 (16 bits)\n容量範圍：$-32,768$ 到 $32,767$",[20,11952,11954],{"id":11953},"長整數-long","長整數 long",[89,11956,11957],{},[11,11958,11959],{},"Stack 佔用大小：8 位元組 (64 bits) ── 體積是 int 的雙倍！\n容量範圍：約正負 900 兆（天文數字）",[20,11961,11963],{"id":11962},"無符號整數-unsigned","無符號整數 Unsigned",[122,11965,11966],{},[125,11967,11968,11969],{},"在上述的名字前面加上一個 u，代表 Unsigned（無符號，純正數）。\n",[122,11970,11971],{},[125,11972,11973,11974],{},"它們物理體積不變，但因為放棄了負數，所以正數容量直接翻倍：\n",[122,11975,11976,11979,11982],{},[125,11977,11978],{},"ushort：2 位元組，範圍變成 $0$ 到 $65,535$。",[125,11980,11981],{},"uint：4 位元組，範圍變成 $0$ 到約 42 億。",[125,11983,11984],{},"ulong：8 位元組，範圍變成 $0$ 到約 1800 兆。",[20,11986,11987],{"id":11987},"byte",[89,11989,11990],{},[11,11991,11992],{},"最小的整數\n只佔 1 位元組 (8 bits)，只能存 $0$ 到 $255$。",[272,11994],{},[85,11996,329],{"id":11997},"list",[89,11999,12000],{},[11,12001,12002],{},"是一個標準的參考型別",[20,12004,12006],{"id":12005},"與csharp自身的array差異","與Csharp自身的Array差異",[89,12008,12009],{},[11,12010,12011],{},"原生 Array 的讀寫速度比 List 快了約 15% 到 30%。",[89,12013,12014],{},[11,12015,12016,12017],{},"在「底層硬體與生態系」上，它們的地位完全不重疊。因為 List",[12018,12019,12020],"t",{}," 本質上只是個「寄生蟲」，它的內部肉身，百分之百是靠原生 Array 撐起來的！",[28,12022,12024],{"className":30,"code":12023,"language":32,"meta":33,"style":33},"public class List\u003CT>\n{\n    internal T[] _items; \u002F\u002F 👈 抓到了！骨子裡就是一個最普通的原生陣列！\n    internal int _size;  \u002F\u002F 記錄目前真正裝了幾個格子\n}\n",[35,12025,12026,12031,12035,12040,12045],{"__ignoreMap":33},[38,12027,12028],{"class":40,"line":41},[38,12029,12030],{},"public class List\u003CT>\n",[38,12032,12033],{"class":40,"line":47},[38,12034,165],{},[38,12036,12037],{"class":40,"line":62},[38,12038,12039],{},"    internal T[] _items; \u002F\u002F 👈 抓到了！骨子裡就是一個最普通的原生陣列！\n",[38,12041,12042],{"class":40,"line":173},[38,12043,12044],{},"    internal int _size;  \u002F\u002F 記錄目前真正裝了幾個格子\n",[38,12046,12047],{"class":40,"line":179},[38,12048,270],{},[122,12050,12051],{},[125,12052,12053,12054],{},"假設宣告 var myList = new List",[1401,12055,12056,12057],{},"();\n",[122,12058,12059,12066],{},[125,12060,12061,12062,12065],{},"宿主出生：List 在 Heap 大倉庫裡悄悄 new int",[38,12063,12064],{},"4"," 蓋了一個長度只有 4 的原生 Array（叫底層陣列）。",[125,12067,12068,12069,12072],{},"動態擴容（Resize）：當你塞滿 4 個格子，想塞第 5 個時，List 會在後台發動大絕招──在 Heap 另一個地方 new int",[38,12070,12071],{},"8"," 蓋一個雙倍大的新原生 Array，把舊的 4 個資料影印過去，然後把舊的 Array 丟給 GC 丟掉。",[11,12074,12075],{},[3541,12076],{"alt":3543,"src":12077},"https:\u002F\u002Fhackmd.io\u002F_uploads\u002FSyHAe9fgMl.png",[11,12079,4182],{},[20,12081,12083],{"id":12082},"跟js的array非常類似","跟JS的Array非常類似",[122,12085,12086,12110],{},[125,12087,12088,12089],{},"強型別的「單一品種限制」（最核心的差別）",[122,12090,12091,12098],{},[125,12092,12093,12094,12097],{},"JS 的 Array（自由散漫）：\n一個陣列裡可以同時塞字串、數字、甚至是物件：",[38,12095,12096],{},"\"Alice\", 30, { isOk: true }","。因為 JS 骨子裡沒有強型別鐵律。",[125,12099,12100,12101],{},"C# 的 List",[12018,12102,12103,12104],{},"（基因純正）：\n你必須在角括號 ",[12018,12105,12106,12107],{}," 裡死死指定這個櫃子只能裝什麼。一旦宣告 List",[1401,12108,12109],{},"，如果你敢把 \"Alice\" 塞進去，編譯器在編譯那一秒就會直接物理性亮紅燈報警！這保證了櫃子裡所有資料的形狀與大小完全一致。",[125,12111,12112,12113],{},"底層的「連續圈地大作戰」",[122,12114,12115,12118],{},[125,12116,12117],{},"JS 的 Array（骨子裡其實是雜湊表\u002F物件）：JS 的陣列在記憶體裡可能是不連續的（Sparse Array），它是靠 Key-Value 的對照表去模擬陣列。",[125,12119,12100,12120],{},[12018,12121,12122,12123],{},"（骨子裡包著一個實體靜態 Array）：\n這就是最有趣的硬體真相。當你 new List",[1401,12124,12125],{},"() 時，C# 其實是在 Heap 大倉庫裡，悄悄圈了一塊絕對連續、死板的「實體靜態陣列 (Array)」空間（預設容量通常是 4）。",[28,12127,12129],{"className":30,"code":12128,"language":32,"meta":33,"style":33},"List\u003Cstring> names = [\"\u003Cname>\", \"Ana\", \"Felipe\"];\nforeach (var name in names)\n{\n    Console.WriteLine($\"Hello {name.ToUpper()}!\");\n}\n",[35,12130,12131,12136,12141,12145,12150],{"__ignoreMap":33},[38,12132,12133],{"class":40,"line":41},[38,12134,12135],{},"List\u003Cstring> names = [\"\u003Cname>\", \"Ana\", \"Felipe\"];\n",[38,12137,12138],{"class":40,"line":47},[38,12139,12140],{},"foreach (var name in names)\n",[38,12142,12143],{"class":40,"line":62},[38,12144,165],{},[38,12146,12147],{"class":40,"line":173},[38,12148,12149],{},"    Console.WriteLine($\"Hello {name.ToUpper()}!\");\n",[38,12151,12152],{"class":40,"line":179},[38,12153,270],{},[28,12155,12157],{"className":30,"code":12156,"language":32,"meta":33,"style":33},"    \nConsole.WriteLine();\nnames.Add(\"Maria\");\nnames.Add(\"Bill\");\nnames.Remove(\"Ana\");\nforeach (var name in names)\n{\n    Console.WriteLine($\"Hello {name.ToUpper()}!\");\n}\n    \n",[35,12158,12159,12163,12168,12173,12178,12183,12187,12191,12195],{"__ignoreMap":33},[38,12160,12161],{"class":40,"line":41},[38,12162,2371],{},[38,12164,12165],{"class":40,"line":47},[38,12166,12167],{},"Console.WriteLine();\n",[38,12169,12170],{"class":40,"line":62},[38,12171,12172],{},"names.Add(\"Maria\");\n",[38,12174,12175],{"class":40,"line":173},[38,12176,12177],{},"names.Add(\"Bill\");\n",[38,12179,12180],{"class":40,"line":179},[38,12181,12182],{},"names.Remove(\"Ana\");\n",[38,12184,12185],{"class":40,"line":185},[38,12186,12140],{},[38,12188,12189],{"class":40,"line":191},[38,12190,165],{},[38,12192,12193],{"class":40,"line":197},[38,12194,12149],{},[38,12196,12197],{"class":40,"line":203},[38,12198,270],{},[11,12200,12201],{},[3541,12202],{"alt":3543,"src":12203},"https:\u002F\u002Fhackmd.io\u002F_uploads\u002FSJjg40bgGg.png",[272,12205],{},[85,12207,824],{"id":12208},"dictionary",[20,12210,12212],{"id":12211},"dictionary-的-key","Dictionary 的 Key",[89,12214,12215],{},[11,12216,12217,12218,3488,12221],{},"要在 C# 裡面有資格當 Dictionary 的 Key，在物理和邏輯上必須通過兩大硬性考驗",[35,12219,12220],{},"Equals",[35,12222,12223],{},"GetHashCode",[11,12225,12226],{},"🎟️ 通行證一：Equals（它必須能精準比對「我們一不一樣」）\n字典在找 Key 的時候，就像管理員在核對身分。",[11,12228,12229],{},"如果 Key 是參考型別（物件），電腦預設比對的是「記憶體位址」。如果用物件當 Key，你兩次宣告 new Person(\"Alice\") 就算內容一樣，因為在 Stack 上的位址不同，字典就會判定它們是不同的 Key。",[11,12231,12232],{},"但 Tuple 是值型別！C# 官方在設計 ValueTuple 時，特別幫它寫好了物理規則：「只要裡面的每一個元素內容完全相同，它們在邏輯上就是完全相等的東西。」",[11,12234,12235],{},"你的 target 宣告為 (1, 3)。",[11,12237,12238,12239,142],{},"字典裡本來就存了一個 ",[38,12240,12241],{},"(1, 3)",[11,12243,12244],{},"當 TryGetValue 啟動時，電腦拿著 target 去跟字典裡的 Key 比對。電腦會一格一格檢查：1 == 1（對）、3 == 3（對）。管理員蓋章認證：「完全一致！」，所以能精準找到對應的 \"Sensor A\"。",[11,12246,12247],{},"🎟️ 通行證二：GetHashCode（它能算出專屬的「分類雜湊值」）\n這是字典物理效能極快的核心祕密。字典內部不是一條線慢慢排隊找 Key 的，而是像郵局一樣，有很多個「分類信箱（Buckets）」。",[11,12249,12250],{},"當你把一個 Key 丟進去，電腦會對這個 Key 執行一個數學公式，算出一個整數，叫做 HashCode（雜湊值）。這個數字決定了這個 Key 該去哪一個信箱。",[11,12252,12253],{},"以前大家喜歡用 string 當 Key，因為字串算出來的 HashCode 很穩定。",[11,12255,12256],{},"微軟在設計 ValueTuple 時，也幫它內建了超厲害的算式：它會把 Row (1) 和 Column (3) 的記憶體二進位訊號混在一起，算出一串專屬於 (1, 3) 的唯一 HashCode 數字。",[11,12258,12259],{},"因為只要內容是 (1, 3)，算出來的 HashCode 永遠一模一樣，所以 TryGetValue 就能以極速、點對點地直接衝到正確的信箱裡把資料拔出來。",[272,12261],{},[56,12263,58],{},{"title":33,"searchDepth":47,"depth":47,"links":12265},[12266,12275,12279],{"id":11845,"depth":47,"text":11845,"children":12267},[12268,12269,12270,12271,12272,12273,12274],{"id":11848,"depth":62,"text":11849},{"id":11852,"depth":62,"text":11853},{"id":11872,"depth":62,"text":11873},{"id":11944,"depth":62,"text":11945},{"id":11953,"depth":62,"text":11954},{"id":11962,"depth":62,"text":11963},{"id":11987,"depth":62,"text":11987},{"id":11997,"depth":47,"text":329,"children":12276},[12277,12278],{"id":12005,"depth":62,"text":12006},{"id":12082,"depth":62,"text":12083},{"id":12208,"depth":47,"text":824,"children":12280},[12281],{"id":12211,"depth":62,"text":12212},"`浮點數`適合表示大小可非常大或非常小的非整數",{},"\u002Fnotes\u002Fsync\u002Fcsharp-c",{"title":11840,"description":12282},"C#\u002FC# 基本型別.md","notes\u002Fsync\u002Fcsharp-c-基本型別",[76],"arKU5-Kp_21O0TluP2FM1rYCEy5Pg8-sGgq6wqkQTjw",1788767746657]