使用的場合:
便於保存,把持有運行狀態的對象系列化後保存到本地,在下次運行程序時,反系列化該對象來恢復狀態
便於傳輸,在網絡中傳輸系列化後的對象,接收方反系列化該對象還原
複製黏貼,複製到剪貼板,而後黏貼html
用來輔助系列化和反系列化的特性:在System.Runtime.Serialization
命名空間下
OnDeserialized
,應用於某個方法,該方法會在反系列化後當即被自動調用(可用於處理生成的對象的成員)
OnDeserializing
,應用於某個方法,該方法會在執行反系列化時被自動調用
OnSerialized
,應用於某個方法,對象在被系列化後調用該方法
OnSerializing
,應用於某個方法,在系列化對象前調用該方法
若是以上輔助特性仍不能知足需求,那就要爲目標對象實現ISerializable
接口了算法
ISerializable
接口:該接口運行對象本身控制系列化與反系列化的過程(實現該接口的同時也必須應用Serializable
特性)數據庫
原理:若系列化一個對象時,發現對象實現了ISerializable
接口,則會忽略掉類型全部的系列化特性應用,轉而調用類型的GetObjectData()
接口方法,該方法會構造一個SerializationInfo
對象,方法內部負責對該對象設置須要系列化的字段,而後系列化器根據該對象來系列化。反系列化時,若發現反系列化後的對象實現了ISerializable
接口,則反系列化器會把數據反系列化爲SerializationInfo
類型的對象,而後調用匹配的構造函數來構造目標類型的對象。編程
系列化:須要實現GetObjectData()
方法,該方法在對象被系列化時自動被調用
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context){ }
c#
反序列化:須要爲它定義一個帶參數的受保護的構造函數,用於反系列化後從新構造對象
protected Person(SerializationInfo info, StreamingContext context) { }
利用該接口,能夠實現將數據流反系列化爲其餘任意指定對象(在原對象定義GetObjectData()
方法,在目標對象定義用於反系列化的構造函數,但兩個對象都必須實現ISerializable
接口)
注意:
若父類爲實現ISerializable
接口,只有子類實現ISerializable
接口,若想系列化從父類繼承的字段,則須要在子類的反系列化構造器中和GetObjectData()
方法中,添加繼承自父類的字段的處理代碼
若父類也實現了ISerializable
接口,則只需在子類的反系列化構造器中和GetObjectData()
方法中調用父類的版本便可windows
public class Class1 { public static void Main() { Person person = new Person() { FirstName = "RuiFu", LastName = "Su"}; //系列化person對象並存進文件中,MyBinarySerializer爲自定義工具類 MyBinarySerializer.SerializeToFile<Person>(person, @"c:\", "Person.txt"); //從文件中取出數據反系列化爲Man類型的對象 Man man = MyBinarySerializer.DeserializeFromFile<Man>(@"c:\Person.txt"); Console.WriteLine(man.Name); Console.ReadKey(); } } [Serializable] public class Man:ISerializable { public string Name; protected Man(SerializationInfo info,StreamingContext context) { Name = info.GetString("Name"); } void ISerializable.GetObjectData(SerializationInfo info,StreamingContext context) { } } [Serializable] public class Person:ISerializable { public string FirstName; public string LastName; void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { //設置反系列化後的對象類型 info.SetType(typeof(Man)); //根據原對象的成員來爲info對象添加字段(這些字段將被系列化) info.AddValue("Name", string.Format("{0} {1}", LastName, FirstName)); } }
不該該被系列化的成員:
爲了節省空間、流量,若是一個字段反系列化後對保存狀態無心義,就不必系列化它
若是一個字段能夠經過其它字段推算出來,則不必系列化它,而用OnDeserializedAttribute
特性來觸發推算方法執行
對於私密信息不該該被系列化
若成員對應的類型自己未被設置爲可系列化,則應該把他標註爲不可系列化[NonSerialized]
,不然運行時會拋出SerializationException
把屬性設置爲不可系列化:把它的後備字段設置爲不可系列化便可實現
把事件設置爲不可系列化:[field:NonSerialized]
網絡
正常系列化與反系列化示例:自定義了工具類MyBinarySerializer
多線程
using System; using System.Collections.Generic; using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Serialization; public class Class1 { public static void Main() { Person person1 = new Person() { FirstName = "RuiFu", LastName = "Su", FullName = "Su RuiFU",IDCode="0377"}; //系列化person1並存進文件中 MyBinarySerializer.SerializeToFile<Person>(person1, @"c:\", "Person.txt"); //從文件中取出數據反系列化爲對象(文件中不含FullName信息,但系列化後自動執行了預約義的推算方法) Person person2 = MyBinarySerializer.DeserializeFromFile<Person>(@"c:\Person.txt"); Console.WriteLine(person2.FullName); Console.ReadKey(); } } [Serializable] public class Person { public string FirstName; public string LastName; [NonSerialized] //禁止被系列化 public string FullName; //可被以上2個字段推算出來 [OnDeserialized] //反系列化後將被調用的方法 void GetFullName(StreamingContext context) { FullName = string.Format("{0} {1}", LastName, FirstName); } [NonSerialized] private string idCode; public string IDCode { get { return idCode; } set { idCode = value; } } [field: NonSerialized] public event EventHandler NameChanged; } //自定義的系列化與反系列化工具類 public class MyBinarySerializer { //將類型系列化爲字符串 public static string Serialize<T>(T t) { using(MemoryStream stream = new MemoryStream()) { BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(stream, t); return System.Text.Encoding.UTF8.GetString(stream.ToArray()); } } //將類型系列化爲文件 public static void SerializeToFile<T>(T t, string path, string fullName) { if(!Directory.Exists(path)) { Directory.CreateDirectory(path); } string fullPath = string.Format(@"{0}\{1}", path, fullName); using(FileStream stream = new FileStream(fullPath,FileMode.OpenOrCreate)) { BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(stream, t); stream.Flush(); } } //將字符串反系列化爲類型 public static TResult Deserialize<TResult>(string s) where TResult: class { byte[] bs = System.Text.Encoding.UTF8.GetBytes(s); using(MemoryStream stream = new MemoryStream()) { BinaryFormatter formatter = new BinaryFormatter(); return formatter.Deserialize(stream) as TResult; } } //將文件反系列化爲類型 public static TResult DeserializeFromFile<TResult>(string path) where TResult: class { using(FileStream stream = new FileStream(path,FileMode.Open)) { BinaryFormatter formatter = new BinaryFormatter(); return formatter.Deserialize(stream) as TResult; } } }
private bool CheckNumber(int number, ref string message) { if(number < 0) { message = "number不能爲負數。"; return false; } else if(number > 100) { message = "number不能大於100。"; return false; } return true; } //調用: string msg = string.Empty; if(CheckNumber(59, ref msg) { //正常邏輯處理代碼 }
AppDomain.UnHandledException
事件,該事件會接收到未處理異常的通知從而調用在它上面註冊的方法,而後應用程序退出(註冊方法沒法阻止應用程序的退出,咱們只能利用該方法來記錄日誌)Application.ThreadException
事件來處理窗體線程中所發生的未被處理的異常,用AppDomain.UnHandledException
事件來處理非窗體線程中發生的未被處理的異常。ThreadException
事件能夠阻止應用程序的退出。static Action<Exception> action;//直接用預約義的Action委託類 static Exception exception; public static void Main() { action = CatchThreadException; //註冊方法 Thread t1 = new Thread(new ThreadStart(delegate { try { Console.WriteLine("子線程執行!"); throw new Exception("子線程t1異常"); } catch(Exception ex) { OnCatchThreadException(ex); //執行方法 //若是是windows窗體程序,則能夠直接用以下方法: //this.BeginInvoke((Action)delegate //{ // throw ex; //將在主線程引起Application.ThreadException //} } })); t1.Start(); t1.Join();//等待子線程t1執行完畢後,再返回主線程執行 if(exception!=null) { Console.WriteLine("主線程:{0}", exception.Message); } Console.ReadKey(); } static void CatchThreadException(Exception ex) { exception = ex; } static void OnCatchThreadException(Exception ex) //定義觸發方法 { var actionCopy = action; if(actionCopy!=null) { actionCopy(ex); //觸發!!! } }
System.Exception
類或其它常見的基本異常,並讓你的異常類應用[Serializable]
,這樣就能夠在須要的時候系列化異常(也能夠對異常類實現ISerializable
接口來自定義系列化過程)[Serializable] public class MyException : Exception { public MyException() { } public MyException(string message) : base(message) { } public MyException(string message, Exception inner) : base(message, inner) { } //用於在反系列化時構造該異常類的實例 protected MyException( SerializationInfo info, StreamingContext context) : base(info, context) { } }
//1.值類型 public static int SomeMethod(int a) { try { a = 10; return a; } finally { a = 100; Console.WriteLine("a={0}", a); } } //調用 Console.WriteLine(SomeMethod(1)); //a=100 //10 這是方法的返回值,finally沒法修改返回值 //2.引用類型 public class Person:ISerializable { public string FirstName; } public static Person SomeMethod(Person a) { try { a.FirstName = "Wang"; return a; } finally { a.FirstName = "Su"; a = null; Console.WriteLine("a={0}", a); } } //調用 Person person = new Person(); Console.WriteLine(SomeMethod(person).FirstName); //a= //Su finally成功修改了對象的字段,但對引用a自己的改變不影響返回值對象
System.Threading.Thread
類開闢的線程,用完就自行銷燬,不可重用。主要用於密集型複雜運算System.Threading.ThreadPool
類管理的一組線程,可重用。主要用於I/O等異步操做。線程池中的一條線程任務完成後,該線程不會自行銷燬。相反,它會以掛起狀態返回線程池。若是應用程序再次向線程池發出請求,那麼這個掛起的線程將激活並執行任務,而不會建立新線程。這節約了不少開銷。只要線程池中應用程序任務的排隊速度低於一個線程處理每項任務的速度,那麼就能夠反覆重用同一線程,從而在應用程序生存期內節約大量開銷。BeginInvoke()
方法,執行該方法時,在線程池ThreadPool
中啓用一條線程來處理任務,完成後會調用方法參數中指定的回掉函數,線程池中的線程是分配好的,使用時不須要new操做)WaitHandle
,包括Semaphore
、Mutex
、EventWaitHandle
(子類AutoResetEvent
、ManualResetEvent
),他們的原理都同樣,都是維護一個系統內核句柄。EventWaitHandle
維護一個由內核產生的布爾變量(阻滯狀態),false表示阻塞線程,true則解除阻塞。它的子類AutoResetEvent
在執行完Set()方法後會自動還原狀態(每次只給一個WaitOne()
方法發信號),而ManualResetEvent
類在執行Set()
後不會再改變狀態,它的全部WaitOne()
方法都能收到信號。只要WaitOne()
未收到信號,它就一直阻塞當前線程,以下示例:public static void Main() { AutoResetEvent autoReset = new AutoResetEvent(false); ManualResetEvent manualReset = new ManualResetEvent(false); Thread t1 = new Thread(new ThreadStart(() => { autoReset.WaitOne(); Console.WriteLine("線程t1收到autoReset信號!"); })); Thread t2 = new Thread(new ThreadStart(() => { autoReset.WaitOne(); Console.WriteLine("線程t2收到autoReset信號!"); })); t1.Start(); t2.Start(); Thread.Sleep(1000); autoReset.Set();//t1 t2 只能有一個收到信號 Thread t3 = new Thread(new ThreadStart(() => { manualReset.WaitOne(); Console.WriteLine("線程t3收到manualReset信號!"); })); Thread t4 = new Thread(new ThreadStart(() => { manualReset.WaitOne(); Console.WriteLine("線程t4收到manualReset信號!"); })); t3.Start(); t4.Start(); Thread.Sleep(1000); manualReset.Set();//t3 t4都能收到信號 Console.ReadKey(); }
方法一:調用線程執行方法,在方法中實現死循環,每一個循環用Thread.Sleep()
設定阻塞時間(或用thread.Join()
);
方法二:使用System.Timers.Timer
類;
方法三:使用System.Threading.Timer
;
具體怎麼實現,就不細說了,看MSDN,或百度併發