發表文章

目前顯示的是有「C#」標籤的文章

命名規則:變數前是否要加上型別資訊

最早最早以前,一開始接觸程式設計時,就是在用Visual C++ 5.0 + MFC寫Windows程式。所以一開始的程式變數的命名規則都是學MFC裏的命名方式,也就是所謂 匈牙利命名法 。 匈牙利命名法倒是也沒什麼問題,我想他的起源應該是因為以前的C編譯器是不支援在編譯時檢查參數型別的,所以特別要在變數的前面加上型別的資訊,以便程式設計人員可以在編寫程式碼的時候就能夠有機會在第一時間查覺出所引用的變數型別上有出入,而不用等到程式真正編譯好執行時才發現錯誤。 而目前的程式語言及編譯器都已經能在編譯程式碼時就幫忙檢查引用的參數型別了,於是這種命名方式隨著時代的演進,應該是可以不用採用這種方式來命名了才對。 就開發程式設段時間以來,目前最常用到的就是兩種命名方式,一種是Pascal Case,另一種是Camel Case。 Pascal Case 變數的命名首字母是大寫,後續單字的第一個字母也都是大寫,例如TestSample。 Camel Case 有分成兩種,一種是Upper Camel Case,基本上就跟Pascal Case一樣。另一種是Lower Camel Case,變數的首字母是小寫,後續單字的第一個字母則都是大寫。例如testSample。 基本上這樣的方式也沒有誰好誰壞,只要有個規則,大家一致就好。 不過個人認為,如果是無型別檢查的環境下,變數的名稱(或一些需要命名的名稱)我習慣會加上型別的資訊。 例如在使用資料庫時,要為資料表及檢視表命名時,習慣上會加上tbl及vw的字首。用來識別這是資料表還是檢視表。 因為有些語法在檢視表上應該是不適用的,例如INSERT、UPDATE、DELETE這類的語法。檢視表只適合用來下SELECT的查詢語法。為了在使用時就能識別出是資料表或檢視表,個人覺得還是加上識別字比較好。 在C#或C++中我們可以宣告一個列舉,例如: public enum ServerStatusEnum { READY, IDLE } 在使用這種列舉型別來定義變數時,就可以: public ServerStatusEnum ServerStatus = ServerStatusEnum.IDLE; 因為我們想要的就是定義一個Server的Status,所以變數名稱當...

沒有預設建構子無法對List<>進行xml序列化

圖片
突然需要寫個xml的設定檔,於是就簡單寫了一個類別TestXml,其中含有一個List<TestA>的成員。TestA也是個簡單的類別,只有兩個屬性及一個帶有參數的建構子。 針對Xml的序列化也是簡單地使用XmlSerialize類別來處理,程式碼如下。但是一執行就出現了Exception。 Exception的內容是這樣寫的,它說無法序列化List<>中的型別。 一開始以為是TestA類別上要加上[Serializable],結果加了也沒用。後來又加了XmlArray、XmlArrayItem等等的,還是沒用。 最後就放棄List<>,先針對單一的類別TestA來進行Xml序列化,才知道原來是TestA這個類別沒有預設建構子(就是不帶任何參數的建構子)。找了半天,原來是這個問題。一切只能說List<TestA>無法序列化時的錯誤訊息也太不直覺了吧。

XmlSerializer需要宣告所有衍生型別

圖片
以前一直以為只要寫好Class的宣告,再用XmlSerializer::Serialize()及XmlSerializer::Deserializer()就可以很簡單地將物件轉成xml格式來匯出及匯入。沒想到,有一次在使用時,只是多加了一個衍生類別,就造成匯出xml時出現例外錯誤。 錯誤訊息如下: 看來是說不需要我新加的衍生類別,但又說要使用XmlInclude。於是就查了一下XmlInclude,原本如果有新增的衍生類別(有一個以上的衍生類別)的話,就要在基底類別加上用XmlInclude宣告的屬性。 如果不想要宣告在基底類別的話, 可以用另一個XmlSerializer的建構子,傳入所要使用的衍生類別的型別 。 使用第二種方式的好處是,當你自行再衍生出其它新的類別,而不是當初已知的類別時,就可以用第二種方式來 動態地 加入新的衍生類別。

實作C# Form的登出再重新登入功能

一般在開發應用程式時都會在執行程式時先顯示一個對話視窗要求使用者輸入登入資訊。正確後再登入到主畫面中。 在主畫面中也會登供一個登出的功能,以便讓使用者重新登入。看是用其它的使用者帳號登入或是登入到不同的環境中。 不過在C#中好像沒有辦法直接支援這種登出登入模式。所以必須自行實作。目前我實作的方式就是在主視窗中宣告一個公用變數來記錄是否要進行重新登入,並接收FormClosiing事件詢問使用者是否要重新登入。 程式碼如下: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace TestFormLogout { public partial class Form1 : Form { /// <summary> /// 記錄是否要進行重新登入 /// </summary> public bool isReLogin = false ; public Form1() { InitializeComponent(); } private void btnLogout_Click( object sender, EventArgs e) { this .Close(); } private void OnClosing( object sender, FormClosingEventArgs e) { if (MessageBox.Show( "是否重新登入?" , "重新登入" , MessageBoxButtons.YesNo) == DialogResult.Yes)...

C# Controls相關文章

EasyProgressBar

解決未取消Event註冊時造成物件無法被回收的議題

當我們將A物件的某函式掛勾到B物件的某個事件上時,如果沒有將B物件的事件函式中有關A物件的掛勾關聯取消的話,就算A物件已不在可用範圍,需要被進行回收的話,因為它還存在著B物件對它的參考,會造成A物件一直無法被回收。也就是所謂的memory leaking。 要解決這個問題,可以參考: Simple effective Weak Event Dispatcher in C#

Windows系統路徑及C#存取方式

圖片
在Windows系統中有許多常用的路徑,但是Program Files或System32等等。這些路徑其實Windows系統本身有提供 路徑環境變數 ,可在命令列視窗中使用。 在C#中也可以利用 Environment這個類別 來存取及轉換這些特別的路徑。 還有介紹如何 在PowerShell中取得這些特別路徑 的方式。

不要在建構子中呼叫虛擬函式

圖片
最近發現當在建構子中設計要去呼叫虛擬函式時,會發生衍生類別因為還未完全執行完建構子的初始化動作,就直接呼叫它所改寫的虛擬函式的話,可能會引發一些問題。 例如: using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace VS2020Test { public class CallVirtualFuncFromCtor { public static void UT() { MyDerivative md = new MyDerivative(2, 5); } } public class MyBase { protected int id = 0; public MyBase( int i) { id = i; ShowID(); } public virtual void ShowID() { Console.WriteLine( "[Base] ID " + id.ToString()); } } public class MyDerivative : MyBase { protected int id2 = 0; public MyDerivative( int id, int id2) : base (id) { this .id2 = id2; } public override void ShowID() { Console.WriteLine( "[Derivative] ID " + id.ToString() +...

XPTable接收資料修改的事件

圖片
XPTable 算是一個不錯用的免費套件,它是用C#語言寫成的。不過好像好久沒有更新了。 最近在使用時,需要接收XPTable中每個Cell的變動資訊。一開始是使用XPTable本身的一個事件:EditingStopped。可是這個事件雖然是在編輯結束時會收到通知,但在此時,XPTable並未將資料放入到Cell中。所以根本就沒有機會取得目前最新的資訊。 後來才發現,原本是要為每個Cell去接收它的PropertyChanged的事件。在觸發這個事件時,才會知道新的值為何。

C# 免費函式庫

好用的 C# Docking Control .

List<>.Find() 使用Anonymous Delegate

在.NET 2.0之後開始支援了泛型,而且許多集合類別中也都支援了許多泛型函式。例如List<>中可以使用Find()函式來搜尋在List集合中的資料,將符合搜尋條件的資料給搜尋出來。 雖然List<>.Find()函式定義了要傳入的參數型別:Predicate ,可是在實作上其實有許多種不同的方式。每種方式都有其優缺點,在此針對這些方式提供筆者個人的一些看法給大家參考。 首先先來看一段程式碼: using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestVS2008 { public class AnonyDelegate { public void Test() { List< string > listData = new List< string >(); listData.Add( "Eric" ); listData.Add( "Anny" ); listData.Add( "Sophia" ); string target = "Anny" ; // Search string found = listData.Find( IsMatch ); Console.WriteLine( "Search with Predicate function : " + found); // Search with SearchFunctor found = listData.Find( new SearchFunctor(target).SearchPredicate); Console.WriteLine( ...

.Net Generic中的default(T)跟new T()

先來看一段.Net C#中的generic程式碼。 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestVS2008 { public class TestGeneric { public static T CreateGeneric<T>() where T : new () { return new T(); } public static T CreateGeneric_2<T>() { return default (T); } public static void Test() { int i = CreateGeneric< int >(); TestGeneric t = CreateGeneric<TestGeneric>(); TestGeneric t2 = CreateGeneric_2<TestGeneric>(); } } } 上面這段程式碼中,主要想要測試的是在使用泛型之下, new  T()跟default(T)的差別。 首先看到CreateGeneric_2<T>()這個函式,它針對所需的型別T並沒有加入任何的限制條件。任何型別都可以帶入這個函式來使用。所以當它需要傳回一個T型別的物件時,它只能用default(T)的方式來產生物件。這種方式對一般類別或是value型別都適用。當你的T型別為value型別時,就會傳回所有bit均為0的值。當你的T型別為一般類別時,則會傳回null值。 再來看CreateGeneric<T>()這個函式,它限定說所帶入的T型別需要有預設的建構子。當然value型別也是可以適用,而一般的類別則必須提供預設建構子(不帶有任何...

ReaderWriterLock測試及自動釋放

在進行多執行緒模式的程式開發時,常常會需要針對會共同存取到的資料來進行同步化的動作。 同步化的方式,最常用到的應該是就Critical Section的方式了。 在C#中,要實作Critical Section的模式的話,可以使用Monitor類別,或是使用C#內建的關鍵字:lock。 不過,使用Critical Section模式的話,表示在任何一個時間下,只有一個執行緒可以來存取這項共用的資源。如果多數的執行緒只是要讀取其中的資料的話,不好意思,大家必項排隊等候這項資源才行。如果有這種情況發生的話,那多執行緒的程式設計方式,將無法為您的程式執行效能帶來太多的改善。因為,每個執行緒還是會需要去等候某項資源。 為了解決這個問題,C#中提供了一個ReaderWriterLock的物件。這種物件的特性就是在同一時間下,可以同時有多個執行緒來讀取某項共用資源。而當有其它的執行緒想要去寫入這項資源時,它也必須先排隊等候這項資源。一旦要寫入的執行緒取得資源的存取權限時,其它需要取得唯讀權限的執行緒,將全部被阻擋在外。要等到執行寫入資源的執行緒完成後,其它唯讀的執行緒才能再度取得資源。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace TestVS2008 { public class TestReadWriteLock { protected bool isStop = false ; protected ReaderWriterLock rwLock = new ReaderWriterLock(); public void StartTest() { isStop = false ; for ( int i = 0; i < 4; i++) { Thread t1 = new Thread( new ThreadStart(ReaderFunc)); ...

C#矩陣(Matrix)運算函式庫

有時工作上會需要將Matlab的程式碼轉譯成C#版本。 在Matlab中,大部份都會使用到矩陣運算的功能。而這部份也要能轉譯成C#才行。 目前在CodeProject中有人提供一組不錯的 C#矩陣運算函式庫 ,有需要的人可以去下載來使用看看。

yield的使用及原理

圖片
不久前在點部落格看到有人在說明yield這個關鍵字的用法。 那位作者大致上只是說明yield該如何使用而已,而沒有提到它有什麼好處,以及它的運作原理。 今天突然想要研究yield的用法,就去google了一下。沒想到找到幾篇不錯的文章。趁機記錄下來以便查詢。 在這篇文章中,作者說明了 使用yield的呼叫流程 。基本上,一開始大家的作法都會是先將要傳回的資料整理成一份List,再傳回給外面呼叫的函式來使用。這樣的方式有些缺點: 要耗用一個List的變數及記憶體空間。 要花時間將資料放入到List中,傳回。 而在改用yield之後,可以避免掉以上的兩個缺點。改為傳回一個IEnumerator<>的變數,讓呼叫的函式來使用。因為是IEnumerator,所以事先不用複製一份,像是放在List中這樣傳回。 當然,您也是可以自己提供一個自己實作IEnumerator<>的物件傳回給呼叫端使用。不過,這樣等於您自己還在要寫一段程式碼。 使用yield之後,實作IEnumerator<> 的工作等於是編譯器會幫您在背後進行。至於編譯器所產生的程式碼可看這篇文章中, 作者用反組譯程式所看到的程式碼 那樣。 說到這裏,可能要來寫個範例,大家比較能知道其中的差異及效能上的差別。 程式碼如下: static void TestYield() { Random r = new Random(); List listData = new List(); for (int index = 0; index < 10000; index++) { listData.Add(r.Next()); } Stopwatch watch = new Stopwatch(); watch.Reset(); watch.Start();   // Get data by normal list for (int i = 0; i < 1000; i+...

double NaN的判斷

最近在寫程式時,發現Math.Log()這個函式,如果第一個參數為負值時,就會傳回NaN的結果。 於是我想要判斷當傳回值是NaN時,就進行特別的處理。 我的程式碼如下: double d = Math.Log( -1,10); // 強迫傳回NaN的值。 if(  d == double.NaN ) { // 特別處理 } 結果發現,當d的值為NaN時,if()那行的判斷式卻不成立。真的很令人無法相信。 於是我又寫了另一段測試程式: double d = double.NaN; if(  d == double.NaN ) { // 特別處理 } 各位猜猜結果如何? 我都已經直接指定d 的值就是double.NaN了。可是在if()的判斷式中就是不成立。 後來,沒辦法,只好用如下的方式來解決了。 if( double.IsNaN( d ) ) { } 目前測試的結果,發現只有使用double.IsNaN()函式才能正確地判斷出NaN的值。

C#單元測試--使用Pex

今天看到一篇MSDN上的新文章,在 介紹有關單元測試的資訊 。 微軟團隊最近發表了一套可以配合Visual Studio來使用的單元測試軟體-- Pex 。這套軟體可以針對您所開發完成的程式碼,實際分析其中的程式碼語意,自動地產生所需的單元測試範本及相關的程式。 在Pex的網站中,有許多詳細的說明,而且還有 介紹的影片可線上觀看 。 在程式開發過程中,需求不斷地在變更的事實,算是不可否認的現象之一了。就算再如何地做好需求訪談、分析,也無法避免未來開發過程中,或進入維護階段時,所需面對的功能修正的問題。 為了能確保每次進行的軟體功能修正都能正確無誤,而且就算在加入新功能時,也能同時確保原有功能的正常的話,使用測試導向的開發模式是勢在必行的了。 當然,要程式開發人員再多費心思去撰寫測試程式或準備測試範本的話,可能會多佔用他們的寶貴時間,造成一般開發人員在開發過程中,都不太會去花時間在測試的方面。 有了Pex的輔助,可以將測試流程大量地自動化的話,一定可以改善開發流程及品質的。 Pex使用了參數化單元測試(Parameterized Unit Test -- PUT)的技術。以下節錄自Pex的說明文件: A unit test is a method without parameters represents a test case which typically executes a method of a class-under-test with fixed arguments and verifies that it returns the expected result. A parameterized unit test (PUT) is the straightforward generalization of a unit test by allowing parameters. PUTs make statements about the code’s behavior for an entire set of possible input values, instead of just a single exemplary input value. A unit test is a method without parameters represent...

C# Dictionary.ContainsKey()釋懝

今天在使用C#中的泛型集合Dictionary<TKey,TValue>時,發現它的ContainsKey()竟然不能使用我自訂的Key類別所覆寫的Equals()函式。這點著實令我感到不可思議。 ContainsKey()這個函式我已經使用很久了,可這還是第一次發現ContainsKey()傳回來的結果,令我訝異。 我想要的很簡單,就是我有一個資料型別,而我另外定義一個跟這個資料型別相互配合的Key類別。我想要將相同Key值的資料群組起來。於是我宣告了以下這樣的Dictionary<>: Dictionary<TKey,TValue> dic = new Dictionary<Tkey, TValue>(); 接著我就依續為每筆資料產生它對應的Key物件,將它放到這個Dictionary中。當然我的Key類別有覆寫了Equals()函式,而且我用ContainsKey()函式來判斷是否有相同Key的資料存在這個Dictionary中。可是當程式執行完後,發現沒有一筆資料的Key是相同的。我確認過資料,應該是會有Key是相同的才對。此時的我開始感覺不妙。難道Dictionary不再是我所認識的那個Dictionary了嗎!! 於是我開始查詢網路上相關的資料,有人提到在 MSDN中有相關的說明 。看了之後才恍然大悟,原來是這麼一回事啊。 IEqualityComparer 首先來看一段程式碼,這是我一開始以為可以正確地讓Dictionary<>.ContainsKey()為我找到相同Key值的寫法: class MyKey { public int ID; } static void Test1() { Dictionary dicTest = new Dictionary(); MyKey key1 = new MyKey(); key1.ID = 10; dicTest.Add(key1, "Test"); MyKey key2 = new MyKey(...

小心Math.Round()

double d = Math.Round(20.5); d = Math.Round(21.5); 在C#中,執行上面的兩行程式會出現什麼結果呢? Math.Round()一般給人的感覺就是要進行四捨五入。所以就會想說,那應該是 21.0跟 22.0吧。 可是在C#中執行的結果,卻出乎意料,結果是20.0跟22.0。 怎麼會這樣!! 看起來有點不按牌理出牌。我查了一下MSDN,Math.Round()的運作原理如下: 傳回值 最接近 a 的整數。如果 a 的小數部分落在兩個整數中間,一個是偶數,另一個是奇數, 則會傳回偶數 。 說明 這個方法的行為遵循 IEEE Standard 754 第 4 節。這種捨入有時稱為捨入至最接近值或銀行家捨入。 這樣 會將一致四捨五入單向中點值得出的四捨五入錯誤降至最低。   它的原理是希望能將誤差降到最低,不過,卻跟一般人的想法不一樣。 另外還發現原來Math.Round()的引數不只一個,它還可以有第二個引數。 第二個引數是一個列舉值,叫作 MidPointRounding 。 它有兩個列舉值: AwayFromZero 以及 ToEven 。預設下就是使用ToEven。不過,一般人比較能接受的應該是AwayFromZero吧。 所以下次要使用Math.Round()時,請確定你要的運作模式,以免算出來的結果不是你預期的。