使用的场合:
便于保存,把持有运行状态的对象系列化后保存到本地,在下次运行程序时,反系列化该对象来恢复状态
便于传输,在网络中传输系列化后的对象,接收方反系列化该对象还原
复制黏贴,复制到剪贴板,而后黏贴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,或百度并发