Skip to main content

Posts

Showing posts with the label coding

[探索 10 分鐘] 寫點有關 ASP.NET MVC ViewModel, ViewData, ViewBag, TempData 的代碼

將 ASP.NET MVC 的 Controller 資料要傳給頁面, 或是頁面再轉只給其他頁, 有很多方式, Data Passing Mechanism in MVC Architecture  對於不同的資料拋轉方式整理出非常明確的結論, 在開始介紹代碼之前我們先有基本認知 : 在 ASP.NET MVC 中, 沒有 view state, 沒有 code behind, 沒有 server controls。我們仍需在 MVC 架構中傳遞數據, 有一些機制來傳遞數據如下, 如ViewData, ViewBag, TempData, Session。 ViewData ViewData 是 ControllerBase 類的 property ViewData 用於 Controller 將資料傳遞給對應的 view (Controller to View) ViewData 生命週期只存在於當下的請求, 導頁後就被清掉了 (null) ViewData 是 Dictionary 類別, 繼承 ViewDataDictionary 纇, 要注意字典資料轉型跟 null 問題 Example - ViewData["Key"] = "Value" ViewBag ViewBag 也是 ControllerBase 類的 property ViewData 操作方法須對每個資料使用中括弧, ViewBag 簡化為點運算式 ViewBag 基本上是 ViewData 的包裝類別, 也是用於 Controller 將資料傳遞給對應的View (Controller to View) ViewBag 是 C# 4.0的 dynamic 型別, 可在執行期再判斷真正型別 (C# reflection) ViewBag 生命週期也只存在於當下的請求, 導頁後就被清掉了 (null) Example - ViewBag.Key = "Value" TempData TempData 也是 ControllerBase 類的 property TempData 生命週期除了當下請求, 導頁後仍可續存 (如action to action, controller ...

[探索 3 分鐘] 判斷字串內容是否是全數字或全英文, 效率型做法 (c#)

Question : [密碼欄位檢查] 內容不可全數字或全英文, 範例輸出入如下 (input → output)。 123213123123 → false (因為全數字) adfdsbrr → false (因為全英文) abc123 → true 看似簡單, 但條件後來加了一個, 不可用 regex。以及有個小陷阱, 就是 : 不是去檢查英文或數字, 而是不能清一色英文或數字; 這個有了解嗎 ? 如果單純跑迴圈去檢查英文或數字, 那就誤會了哦。一開始沒想太多也不知道好用的函式, 但覺得 ASCII 滿適合上陣的, 先丟兩個版本。 public static bool ASCIIIsStrongPassword(string s) { s = s.ToLower(); long i; if (long.TryParse(s, out i)) return false; //全數字NG if (s.Any(c => c < 'a' || c > 'z')) return true; //擁有一個非英文就pass return false; } public static bool XORIsStrongPassword(string s) { s = s.ToLower(); bool isDigit = false; bool isLetter = false; for (var i = 0; i < s.Length; ++i) { if (s[i] >= '0' && s[i] <= '9') isDigit = true; else if (s[i] >= 'a' && s[i] <= 'z') isLetter = true; if (isDigit && isLetter) return true; } return !isDigit && !isLetter; } 這兩個算創意版...

[探索 3 分鐘] 檢查字串中最多相同數字的最大數量, 效率型做法 (c#)

Question : 有個固定長度 4 的字串, 內容為數字, 需判斷字串中最多相同數字的最大數量, 有點饒舌, 範例輸出入如下 (input → output)。 1314 → 2 (因為 2 個 1) 8888 → 4 (因為 4 個 8) 5566 → 2 (因為 2 個 5 或 2 個 6) 就是這麼簡單。當下是想到暴力法, 畢竟只有 4 個數字...(其實也沒這麼暴力, 隱含著 divide-and-conquer 的概念)。 public static int MaxSameNumbers(string s) { char[] foo = s.ToArray(); Array.Sort(foo); s = new string(foo); var l = s.Substring(0, 2); var r = s.Substring(2); int countL = 1, countR = 1; if (l[0] == l[1]) countL = 2; if (r[0] == r[1]) countR = 2; if (l[1] == r[0]) return countL + countR; else return Math.Max(countL, countR); } 有點囉嗦, 需要先針對字串排序, 如此一來, 左半邊最多 2 個連號, 右半邊也是, 也就是各邊的最大值都在 1 ~ 2 ; 而最後只需要額外再確認是不是中間相連的數字竟也相同。要注意題目不是都 5566, 1314 這種, 還會有 9555, 8888 這種的, 所以解法呢 ? 針對這種中間有相同數字的 case, 把兩邊的最大值加起來即可。但由於真的太囉嗦了, 換一版字典的寫法。 public static int LinqMaxSameNumbers(string s) { var d = new Dictionary<char, int>(); for(var i = 0; i < s.Length; ++i) { if (d.ContainsKey(s[i])) ++d[s[i]]...

[探索 3 分鐘] 檢查浮點數是否恰為整數, 效率型做法 (c#)

Question : 判斷一個浮點數輸入值, 是不是剛好是整數, 範例輸出入如下 (input → output)。 11.112 → false 11.000 → true  就是這麼簡單。想到兩招, 一個用 Math 來計算, 如果無條件捨去跟無條件進位答案相同, 那肯定本身就是整數; 另一招是用安全的 TryParse 來判斷是否為 float。 public static bool IsIntegerByMath(float f) { return Math.Ceiling(f) == Math.Floor(f); } public static bool IsIntegerByParse(float f) { int i; return int.TryParse(f.ToString(), out i); } 如此一來, 程式會動了, 考試應該也有過。但若要追求簡潔或效能的極致, 還有以下的做法。 public static bool IsIntegerByMode(float f) { return f % 1 == 0; } public static bool IsIntegerFloor(float f) { return f == Math.Floor(f); } 網路上的這兩個版本, 直接理解 C# 語言對於浮點數與整數的相互操作, 寫出更高效的寫法。而上面的執行效率各是如何 ? 未測先估 TryParse 是最慢的, 但最快的版本究竟快在哪裡 ? 跑個 1,000 萬次來試試。 答案揭曉, Math 類操作最快, 也就是上述 IsIntegerFloor() 版本; 扣除掉本來就會 BG 的 IsIntegerByParse() 版本, 雖然比剩餘兩個版本快, 但差異幾乎可以忽略。翻 Math.Floor 原始碼 , 原來又是個的 native 函式。 [MethodImplAttribute(MethodImplOptions.InternalCall)] public static extern double Floor(double d); 至於為何要用這種寫法呢, 網路上的  討論  頗多, 但參考  M...

[探索 3 分鐘] 將字串內容反轉輸出, 效率型做法 (c#)

Question : 將字串內容反轉, 範例輸出入如下 (input → output)。 abc → cba  123 → 321 就是這麼簡單。一開始也沒想太多, 就是跑一個迴圈, 從尾巴開始讀每個字元, 用字串累加到頭就結束了。而且沒有遺忘連續字串相加要使用 StringBuilder, 因為效能考量。 public string ReverseByStringBuilder(string s) { var sb = new StringBuilder(); for (var i = s.Length - 1; i >= 0; --i) sb.Append(s[i]); return sb.ToString(); } 另一個版本行數差不多, 但使用 CharArray, 然後又用到  Array.Reverse  方法。 public string ReverseByArray(string s) { char[] charArray = s.ToCharArray(); Array.Reverse(charArray); return new string(charArray); } 也查到  這一版 , 直接用另外一個陣列, 把原來的值拷貝過來, 頭放尾, 尾放頭, 就少了 StringBuilder 類別的開銷了。 public string ReverseByCharBuffer(string s) { char[] c = s.ToCharArray(); for (int i = 0; i < s.Length / 2; ++i) { char t = s[i]; c[i] = s[s.Length - i - 1]; c[s.Length - i - 1] = t; } return new string(c); } 現在好奇兩件事, 上面的執行效率各是如何 ? 最快的版本究竟快在哪裡 ? 跑個 100 萬次來試試。 結果如您想像嗎 ? Array.Reverse() 操作最快, 也就是上述 Re...

[探索 5 分鐘] i++ 與 ++i 的功能與性能是否相同 ? 用程式範例介紹 (c#)

值相同, 但我們知道這兩個 Foo 操作是不同的 Foo( x++ ); // post-increment Foo( ++x ); // pre-increment 大家比較常用的 i++, x++... 我們稱為 post-increment, 是在這一行程式完成之後, 再進行 +1 的動作, 反之稱之為 pre-increment。所以其實上面例子可以理解為如下 (功能上, 非編譯器行為), 更囉嗦的版本。 // Foo ( x++ ) Foo( x ); x = x + 1; // Foo ( ++x ) x = x + 1; Foo( x ); wiki  的例子更清楚, 請特別注意 y 的值。 int x, y; // Increment operators x = 1; y = ++x; // x is now 2, y is also 2 y = x++; // x is now 3, y is 2 這樣看完應該更無懸念了, post-increment 不是等號左邊跑完進行 ++, 也不是等號右邊跑完進行 ++, 就是這一行整個跑完才進行 ++。 // Copy one array to another void copy_array(float *src, float *dst, int n) { while (n-- > 0) // Loop that counts down from n to zero *dst++ = *src++; // Copies element *(src) to *(dst), // then increments both pointers } 以上是說明功能上的不同; 在場景上, 陣列或指標的操作很適合 post-increment, 有「同一行, 同時完成操作並與結束往下一個位置移動, 的簡潔代碼」設計 。性能呢 ? Foo( ++x ) 跟 Foo ( x++ ) 各跑 1 千萬次, 結果是 Foo( ++x ) 這個 pre-increment 呼叫版本性能穩定領先, 非常微小的領先。 附帶一提, 就單一行性能來比較...