.net 组件程序设计
第一章 面向组件编程简介
一个组件是一个.net类
一个对象是一个组件的实例
面向对象编程着眼于被组合到一个大的二进制可执行程序的类之间的关系
面向组件编程着眼于独立工作的可替换的代码模块,并且无须非常熟悉其内部工作原理
优势:
简化软件项目管理
任何对组件的改进,增强,修复都能够马上提供给所有使用组件的应用程序
应用程序更加容易扩展
继承和重用模型上的着重点不同
面向对象白盒重用
面向组件黑盒重用
面向组件编程的原则
接口和实现分离应用程序中的基础单元是二进制兼容接口
二进制兼容性客户端和服务端之间
语言独立性编程语言的选择无关
位置透明客户端代码中没有任何有关对象执行的具体位置
分布式场景部署
并发管理
版本控制
基于组件的安全
第二章 .net 面向组件的编程要素
语言独立性
中间语言和JIT编译器
IL被封装在DLL或EXE中
JIT即时编译器处理把IL转变成真正的机器代码
两类用户:
1. 快速应用程序开发人员 VB
2. 企业应用开发人员 C#
EXE程序集应用程序集
DLL程序集库程序集
一个类可以有两种特性
累积特性是指一个类中的每个部分都可以添加的东西
非累积特性是指类型中的所有部分必须一致的东西
.net二进制兼容性
使用元数据提供这些方法或者字段的所有类型,都是二进制兼容的,因为该类型在内存中实际上的是二进制布局.
第三章 基于接口编程
一个接口不能包含任何实现,接口的所有方法和属性都是抽象的
一个接口不能被实例化,接口等同于一个抽象类
接口和抽象类的区别
1. 一个抽象类仍然可以有实现,它能够有成员变量或非抽象方法或属性,接口不能有实现或成员变量
2. 一个.net类只能从一个基类中派生,即便基类是抽象的,然而,一个.net类可以实现多个接口
3. 抽象类可以从其他类或从一个或多个接口中派生,而接口只能从另一接口中派生
4. 抽象类能够有非公共方法和属性,即便它们都是抽象的。接口所有的都是公共的
5. 抽象类可以有静态方法和静态成员,能定义常量,而接口不能有
6. 抽象类可以有构造函数,接口不可以
显式类型转换:
1. 非范型类工厂
2. 当你希望使用某个类实现的一个接口去获取该类实现的另外一个接口的引用
隐式类型转换: 防御性编程:
IMyInterface obj1;
IMyOtherInterface obj2;
obj1 = newMyClass();
obj1.Method1();
obj2 = obj1 asIMyOtherInterface;
if (obj2 != null)
{
obj2.Method3();
}
接口同样可以定义属性,索引器,和事件
结构实现一个或多个接口
局部类型为一个类定义接口派生,其他类实现
一个接口成员只能由一个部分来实现
类从基类和接口派生时 基类放在第一位
显示实现接口并实现从一个接口引导到另一个
voidIMyOtherInterface.Method1()
{
IMyInterface myInterface = this;
myInterface.Method1();
}
使用 this 引用的方式是通过它自己的实现类来调用显示接口方法的唯一途径
让实现范型类型的类来添加约束,而接口自身保持无约束
范型接口方法只对方法有效,属性和索引器只能在接口定义中使用范型参数
设计和分解接口
最理想的接口成员3-5
接口方法:属性 = 2:1
避免将定义事件作为接口成员
第四章 生命周期管理
托管堆在每个.net的宿主物理进程中,.net运行时都会预先分配一个叫做托管堆的特殊内存堆
通过压缩托管堆并用可到达对象覆盖不可带大对象来实现
publicclassMyClass
{
public MyClass()
{
}
~MyClass()
{
}
}
确定性终结:
开/闭模式 close()
处理Dispose()模式 dispose()
IDisposable()模式
publicinterfaceIMyInterface{
void SomeMethod();}
publicclassMyClass : IMyInterface, IDisposable{
publicvoid SomeMethod(){}
publicvoid Dispose(){}
}
IMyInterface obj = newMyClass();
obj.SomeMethod(); IDisposable disposable = obj asIDisposable; if (obj != null){ disposable.Dispose(); } using (obj){ obj.SomeMethod(); } 接口和using 语句相结合
1. publicinterfaceIMyInterface :IDisposable
{
void SomeMethod();
}
2. using ((IDisposable)obj)
{
obj.SomeMethod();
}
3. using (obj asIDisposable)
{
obj.SomeMethod();
}
第五章 版本控制
兼容性规则:对于强名称程序集,兼容程序集必须具有客户端清单所请求的版本号。对于友好程序集,具有友好名的任何程序集都被认为是兼容的
Assembly assembly = Assembly.GetExecutingAssembly();
Version version = assembly.GetName().Version;
Trace.WriteLine("Version is " + version);
程序集部署模式:
专用安装在应用程序目录
共享安装在全局程序集缓存(GAC)的公共位置
1. 支持并行执行相同程序集的不同版本
2. 多个客户端应用程序之间共享程序集
第六章 事件
委托推断
当添加或删除目标方法时,编译器可以推断委托类型进行实例化。先不将方法名包装在委托对象中,而直接将方法名指定在委托变量中.
publicdelegatevoidNumberChangedEventHandler(int number);
publicclassMyPublisher{
publicNumberChangedEventHandler NumberChanged;}
publicclassMySubscriber{
publicvoid OnNumberChanged(int number){
string message = "New value is " + number;
MessageBox.Show(message, "MySubscriber");}}
MyPublisher publisher = newMyPublisher();
MySubscriber subscriber1 = newMySubscriber();
MySubscriber subscriber2 = newMySubscriber();
publisher.NumberChanged += subscriber1.OnNumberChanged;
publisher.NumberChanged += newNumberChangedEventHandler(subscriber2.OnNumberChanged);
publisher.NumberChanged(3);
publisher.NumberChanged -= newNumberChangedEventHandler(subscriber2.OnNumberChanged);
event 仅有发布类可以触发事件
publicdelegatevoidNumberChangedEventHandler(object sender,EventArgs eventArgs);
publicclassNumberChangedEventArgs : EventArgs{
int m_Number;
public NumberChangedEventArgs(int number){
m_Number = number;}
publicint Number{
get { return m_Number; }
}
}
publicclassMyPublisher{
publiceventNumberChangedEventHandler NumberChanged;
publicvoid FireNewNumberEvent(EventArgs eventArgs){
try{
if (NumberChanged != null){
NumberChanged(this, eventArgs);
}}
catch { }} } publicclassMySubscriber{ publicvoid OnNumberChanged(object sender, EventArgs eventArgs){ NumberChangedEventArgs numberArg = eventArgs asNumberChangedEventArgs; Debug.Assert(numberArg != null); string message = numberArg.Number.ToString(); MessageBox.Show("The new number is " + message); } } MyPublisher publisher = newMyPublisher(); MySubscriber subscriber = newMySubscriber(); publisher.NumberChanged += subscriber.OnNumberChanged; NumberChangedEventArgs numberArg = newNumberChangedEventArgs(4); publisher.FireNewNumberEvent(numberArg); 范型事件
publicdelegatevoidGenericEventHandler(T t,U u);
publicclassMyArgs : EventArgs { }
publicclassMyPublisher{
publiceventGenericEventHandler MyEvent;
publicvoid FireEvent(){
MyArgs args = newMyArgs();
MyEvent(this,args);
}
}
publicclassMySubscriber where A : EventArgs{
publicvoid OnEvent(MyPublisher sender, A args) { }
}
MyPublisher publisher = newMyPublisher();
MySubscriber subscriber = newMySubscriber();
publisher.MyEvent += subscriber.OnEvent;
使用别名
usingMyHandler = GenericEventHandler;
publiceventMyHandler MyEvent;
类型安全的EventsHelper
usingMyEventHandler = GenericEventHandler;
publicdelegatevoidGenericEventHandler();
publicdelegatevoidGenericEventHandler(T t,U u);
publicclassMyPublisher{
publiceventMyEventHandler MyEvent;
publicvoid FireEvent(int number, string str){
EventsHelper.Fire(MyEvent,number,str);
}
}
publicstaticclassEventsHelper{
publicstaticvoid UnsafeFire(Delegate del,paramsobject[] args){
if (del == null){
return;
}
Delegate[] delegates = del.GetInvocationList();
foreach (Delegate sink in delegates){
try{
sink.DynamicInvoke(args);
}
catch { }
}
}
publicstaticvoid Fire(GenericEventHandler del,T t,U u)
{
UnsafeFire(del,t,u);
}
publicstaticvoid Fire(GenericEventHandler del, T t)
{
UnsafeFire(del, t);
}
}
事件访问器
usingMyEventHandler = GenericEventHandler;
publicdelegatevoidGenericEventHandler();
publicdelegatevoidGenericEventHandler(T t);
publicdelegatevoidGenericEventHandler(T t,U u);
publicclassMyPublisher{
MyEventHandler m_MyEvent; publiceventMyEventHandler MyEvent{ add { m_MyEvent += value; } remove { m_MyEvent -= value; } } publicvoid FireEvent(){ EventsHelper.Fire(m_MyEvent,"Hello"); } } publicclassMySubscriber{ publicvoid OnEvent(string message){ MessageBox.Show(message); } } MyPublisher publisher = newMyPublisher(); MySubscriber subscriber = newMySubscriber(); publisher.MyEvent += subscriber.OnEvent; publisher.FireEvent(); publisher.MyEvent -= subscriber.OnEvent; 第七章 异步调用
对象在后台执行方法调用后,控制权立即返回客户端,随后能以某种方式通知客户端该方法已经执行完毕
面向组件的异步调用机制必备条件
1. 即可以用于同步也可以用于异步
2. 由客户端来决定是同步还是异步
3. 客户端能够发起多个异步调用
4. 组件能够服务于多个并发调用
5. 组件的方法有输出参数或是返回值,客户端应该有办法在方法结束时获取他们
6. 发生在组件端的错误能反馈到客户端
7. 异步调用机制方便使用
客户端执行一个异步方法后有以下选择
1. 阻塞直到调用完成
2. 等待方法调用完成
3. 等待一段时间,停止等待
4. 同时等待多个方法的完成
5. 回调函数
publicdelegateintBinaryOperation(int argument1, int argument2);
publicclassCalculator
{
publicint Add(int argument1,int argument2)
{
return argument1 + argument2;
}
publicint Subtract(int argument1, int argument2)
{
return argument1 - argument2;
}
}
Calculator calculator = newCalculator();
BinaryOperation oppDel = calculator.Add;
//oppDel(2,3);
//oppDel.Invoke(2,3);
IAsyncResult asyncResult = oppDel.BeginInvoke(2,3,null,null);
int result = oppDel.EndInvoke(asyncResult);
EndInvoke()在每次异步操作时只能调用一次。
委托类的内部列表中只能允许有一个目标方法
只能在分发调用的那个委托对象上把IAsyncResult对象传递给EndInvoke()
publicclassCalculatorClient
{
IAsyncResult m_AsyncResult;
publicvoid AscuncAdd()
{
Calculator calculator = newCalculator();
DispatchAdd(calculator,2,3);
int result = GetResult();
}
int GetResult()
{
AsyncResult asyncResult = (AsyncResult)m_AsyncResult;
BinaryOperation oppDel = (BinaryOperation)asyncResult.AsyncDelegate;
Debug.Assert(asyncResult.EndInvokeCalled == false);
return oppDel.EndInvoke(m_AsyncResult);
}
void DispatchAdd(Calculator calculator, int argument1, int argument2)
{
BinaryOperation oppDel = calculator.Add;
m_AsyncResult = oppDel.BeginInvoke(argument1, argument2, null, null); } } 阻塞 IAsyncResult asyncResult = oppDel.BeginInvoke(2,3,null,null); while (asyncResult.IsCompleted == false) { asyncResult.AsyncWaitHandle.WaitOne(10,false); } int result = oppDel.EndInvoke(asyncResult); BinaryOperation oppDel1 = calculator.Add; BinaryOperation oppDel2 = calculator.Add; IAsyncResult asyncResult1 = oppDel1.BeginInvoke(2, 3, null, null); IAsyncResult asyncResult2 = oppDel2.BeginInvoke(2, 3, null, null); WaitHandle[] handleArray = { asyncResult1 .AsyncWaitHandle,asyncResult2.AsyncWaitHandle}; WaitHandle.WaitAll(handleArray); 回调方法
publicclassCalculatorClient
{
publicvoid AscuncAdd()
{
Calculator calculator = newCalculator();
BinaryOperation oppDel = calculator.Add;
oppDel.BeginInvoke(2,3,OnMethodCompletion,null);
}
void OnMethodCompletion(IAsyncResult asyncResult)
{
int result = 0;
AsyncResult resultObj = (AsyncResult)asyncResult;
Debug.Assert(resultObj.EndInvokeCalled == false);
BinaryOperation oppDel = (BinaryOperation)resultObj.AsyncDelegate;
result = oppDel.EndInvoke(asyncResult);
Trace.WriteLine("aa" + result);
}
}
参数传递
int asyncsState = 4;
oppDel.BeginInvoke(2, 3, OnMethodCompletion, asyncsState);
int ret = (int)asyncResult.AsyncState;
异步流
异步web
异步错误处理
try
{
result = oppDel.EndInvoke(asyncResult);
Trace.WriteLine("aa" + result);
}
catch { }
异步事件
异步调用的缺陷:
1. 线程并发与同步
2. 线程池的消耗
3. 对引用和引用类型的过早访问
4. 费时的构造函数
5. EndInvoke()后的清理
第八章 多线程和并发管理
一个线程仅仅是一个进程中的一条执行路径
面向cpu的操作用计算机的中央处理器进行密集的或重复的操作
面向I/O 的操作是使用输入或输出设备
创建线程
publicclassMyClass
{
publicvoid ShowMessage()
{
Thread currentThread = Thread.CurrentThread;
string caption = "Thread ID = ";
caption += currentThread.ManagedThreadId;
MessageBox.Show(caption);
}
}
MyClass obj = newMyClass();
//ThreadStart threadStart = obj.ShowMessage;
//Thread workerThread = new Thread(threadStart); Thread workerThread = newThread(obj.ShowMessage); workerThread.Start(); Thread.Sleep(0);强制上下文切换
自动同步
如果一组面向上下文的对象共有一把锁,就说他们在一个同步域中
一个对象可以处于以下任何一个同步域中:
1.在它创建者的同步域中(该对象和它的创建者共享一把锁)
2.在新的同步域中(对象有自己的锁并启动一个新的同步域)
3.完全不在同步域中(因为没有锁,并发访问是允许的)
SynchronizationAttribute.REQUIRED 创建者同步域
SynchronizationAttribute.REQUIRES_NEW 新同步域 类工厂应用
同步域的缺陷:
1. 只能在面向上下问的对象上使用,对于.net的其他类型,仍然手动同步
2. 以代理和拦截器的方式访问面向上下文的对象要付出一定的代价
3. 不能保护静态类成员和静态方法
4. 不是一个面向吞吐量的机制
手动同步
监视器
只和引用类型一块工作的锁
MyClass obj;
Monitor.Enter(obj);
Try
{
obj.ShowMessage();
}
Finnaly
{
Monitor.Exit(obj);
}
保护静态成员和方法
publicclassMyClass
{
staticpublicvoid ShowMessage()
{
}
}
Monitor.Enter(typeof(MyClass));
MyClass.ShowMessage();
Monitor.Exit(typeof(MyClass));
MyClass obj;
lock (obj){obj.ShowMessage();}
publicclassMyClass
{
publicvoid ShowMessage()
{
lock (this)
{
}
}
}
属性不需要加临时变量
publicclassMyClass
{
int m_Number;
publicint Number
{
get { lock(this) { return m_Number; } } } } 同步方法
[MethodImpl(MethodImplOptions.Synchronized)]
publicvoid DoSomething()
{
}
可等待句柄
与Monitor的区别:
Monitor只能在引用类型上使用
可等待句柄同步对值类和引用类型进行访问
可等待句柄多个对象等待是一个原子操作
互斥
命名Mutex过去产物
一个命名Mutex的技巧:
不一定要同步对系统范围资源的访问,而是简单易用的跨进程通信机制,为单例应用程序提供了方便
可等待事件
与Monitor区别:
Monitor受到束缚后没有该动作的任何记忆
事件有记忆,状态保存在信号已发上
信号
Mutex 也叫 二进制 Semaphores
以某种可计算的方式控制线程的执行
互锁
只是增加减少数值或交换比较数值
支持一系列以原子方式访问变量的静态方法
读/写锁
使用Monitor对象锁定整个对象的效率很差,一个宏观的锁会锁定所有对象
使用Mutex效率也很差,如果没有线程写入新数值,多个线程就可以并发的读取当前数据
一个成员变量或属性。经常被多线程读写
同步委托
publiceventEventHandler MyEvent;
publicvoid FireEvent()
{
FireEvent();
}
[MethodImpl(MethodImplOptions.NoInlining)]
void FireEvent(EventHandler handler)
{
if(handler != null)
{
handler(this,EventArgs.Empty);
}
}
使用.net多线程服务
线程相关静态变量
线程本地存储
命名内存槽
未命名内存槽
线程池
工作线程用于处理异步执行或计时器 25
完成端口线程是用来与服务器操作联合的 1000
计时器
第九章 序列化和持久化 序列化当应用程序关闭时,对象状态被保存在文件中
反序列化应用程序启动时加载一些包含数据的文件,并将文件中的信息转化为对象
自动序列化 .net通过反射实现对象的自动序列化每个.net类型的状态大批能够通过反射来读取或设置 .net把对象状态序列化为流
.net序列化是基于对象的只有实例字段被序列化静态字段不行
枚举永远是可序列化的
[Serializable]
publicclassMyClass
[NonSerialized]
当.net 反序列化时,它会将每个不可序列化成员变量初始化为该类型的默认值
[Serializable]
publicclassMyClass :IDeserializationCallback
{
[NonSerialized]
IDbConnection m_Connection;
string m_ConnectionString;
publicvoid OnDeserialization(object sender)
{
m_Connection = newSqlConnection();
m_Connection.ConnectionString = m_ConnectionString;
m_Connection.Open();
}
}
所有委托定义可序列化类
.net带有两个格式器
二进制格式器和soap格式器
自定义序列化
第十章 远程处理
内部网环境下.net remoting
Internet web服务
在托管代码和非托管代码之间的桥梁
应用程序域是建立在非托管进程上的
同一进程的所有应用程序域共享同一托管堆
.net把包含服务器对象的应用程序域称为宿主应用程序域
值传递 引用传递
引用封送激活模式 客户端激活和服务器激活
客户端激活对象
服务端single-call
服务端singleton
客户端代理 透明代理 真实代理
当使用http作为传输协议时,可以选择用SOAP来穿过防火墙
三种协议:
1. tcp
2. http
3. ipc 只能在同一机器的跨进程调用使用
编程式和管理式
第十一章 上下文与拦截
拦截:拦截从客户端到对象的调用,执行某些调用前处理,将调用转发给对象,执行某些调用后处理,然后将控制返回给客户端
上下文敏捷对象
面向上下文对象
组件服务类型
面向上下文服务和企业服务
第十二章 安全
附录A 基于接口的Web服务
附录B 统一windows forms 和asp.net安全
附录C 反射与属性
附录D 范型
附录E c#编码准则
第一章 面向组件编程简介
一个组件是一个.net类
一个对象是一个组件的实例
面向对象编程着眼于被组合到一个大的二进制可执行程序的类之间的关系
面向组件编程着眼于独立工作的可替换的代码模块,并且无须非常熟悉其内部工作原理
优势:
简化软件项目管理
任何对组件的改进,增强,修复都能够马上提供给所有使用组件的应用程序
应用程序更加容易扩展
继承和重用模型上的着重点不同
面向对象白盒重用
面向组件黑盒重用
面向组件编程的原则
接口和实现分离应用程序中的基础单元是二进制兼容接口
二进制兼容性客户端和服务端之间
语言独立性编程语言的选择无关
位置透明客户端代码中没有任何有关对象执行的具体位置
分布式场景部署
并发管理
版本控制
基于组件的安全
第二章 .net 面向组件的编程要素
语言独立性
中间语言和JIT编译器
IL被封装在DLL或EXE中
JIT即时编译器处理把IL转变成真正的机器代码
两类用户:
1. 快速应用程序开发人员 VB
2. 企业应用开发人员 C#
EXE程序集应用程序集
DLL程序集库程序集
一个类可以有两种特性
累积特性是指一个类中的每个部分都可以添加的东西
非累积特性是指类型中的所有部分必须一致的东西
.net二进制兼容性
使用元数据提供这些方法或者字段的所有类型,都是二进制兼容的,因为该类型在内存中实际上的是二进制布局.
第三章 基于接口编程
一个接口不能包含任何实现,接口的所有方法和属性都是抽象的
一个接口不能被实例化,接口等同于一个抽象类
接口和抽象类的区别
1. 一个抽象类仍然可以有实现,它能够有成员变量或非抽象方法或属性,接口不能有实现或成员变量
2. 一个.net类只能从一个基类中派生,即便基类是抽象的,然而,一个.net类可以实现多个接口
3. 抽象类可以从其他类或从一个或多个接口中派生,而接口只能从另一接口中派生
4. 抽象类能够有非公共方法和属性,即便它们都是抽象的。接口所有的都是公共的
5. 抽象类可以有静态方法和静态成员,能定义常量,而接口不能有
6. 抽象类可以有构造函数,接口不可以
显式类型转换:
1. 非范型类工厂
2. 当你希望使用某个类实现的一个接口去获取该类实现的另外一个接口的引用
隐式类型转换: 防御性编程:
IMyInterface obj1;
IMyOtherInterface obj2;
obj1 = newMyClass();
obj1.Method1();
obj2 = obj1 asIMyOtherInterface;
if (obj2 != null)
{
obj2.Method3();
}
接口同样可以定义属性,索引器,和事件
结构实现一个或多个接口
局部类型为一个类定义接口派生,其他类实现
一个接口成员只能由一个部分来实现
类从基类和接口派生时 基类放在第一位
显示实现接口并实现从一个接口引导到另一个
voidIMyOtherInterface.Method1()
{
IMyInterface myInterface = this;
myInterface.Method1();
}
使用 this 引用的方式是通过它自己的实现类来调用显示接口方法的唯一途径
让实现范型类型的类来添加约束,而接口自身保持无约束
范型接口方法只对方法有效,属性和索引器只能在接口定义中使用范型参数
设计和分解接口
最理想的接口成员3-5
接口方法:属性 = 2:1
避免将定义事件作为接口成员
第四章 生命周期管理
托管堆在每个.net的宿主物理进程中,.net运行时都会预先分配一个叫做托管堆的特殊内存堆
通过压缩托管堆并用可到达对象覆盖不可带大对象来实现
publicclassMyClass
{
public MyClass()
{
}
~MyClass()
{
}
}
确定性终结:
开/闭模式 close()
处理Dispose()模式 dispose()
IDisposable()模式
publicinterfaceIMyInterface{
void SomeMethod();}
publicclassMyClass : IMyInterface, IDisposable{
publicvoid SomeMethod(){}
publicvoid Dispose(){}
}
IMyInterface obj = newMyClass();
obj.SomeMethod(); IDisposable disposable = obj asIDisposable; if (obj != null){ disposable.Dispose(); } using (obj){ obj.SomeMethod(); } 接口和using 语句相结合
1. publicinterfaceIMyInterface :IDisposable
{
void SomeMethod();
}
2. using ((IDisposable)obj)
{
obj.SomeMethod();
}
3. using (obj asIDisposable)
{
obj.SomeMethod();
}
第五章 版本控制
兼容性规则:对于强名称程序集,兼容程序集必须具有客户端清单所请求的版本号。对于友好程序集,具有友好名的任何程序集都被认为是兼容的
Assembly assembly = Assembly.GetExecutingAssembly();
Version version = assembly.GetName().Version;
Trace.WriteLine("Version is " + version);
程序集部署模式:
专用安装在应用程序目录
共享安装在全局程序集缓存(GAC)的公共位置
1. 支持并行执行相同程序集的不同版本
2. 多个客户端应用程序之间共享程序集
第六章 事件
委托推断
当添加或删除目标方法时,编译器可以推断委托类型进行实例化。先不将方法名包装在委托对象中,而直接将方法名指定在委托变量中.
publicdelegatevoidNumberChangedEventHandler(int number);
publicclassMyPublisher{
publicNumberChangedEventHandler NumberChanged;}
publicclassMySubscriber{
publicvoid OnNumberChanged(int number){
string message = "New value is " + number;
MessageBox.Show(message, "MySubscriber");}}
MyPublisher publisher = newMyPublisher();
MySubscriber subscriber1 = newMySubscriber();
MySubscriber subscriber2 = newMySubscriber();
publisher.NumberChanged += subscriber1.OnNumberChanged;
publisher.NumberChanged += newNumberChangedEventHandler(subscriber2.OnNumberChanged);
publisher.NumberChanged(3);
publisher.NumberChanged -= newNumberChangedEventHandler(subscriber2.OnNumberChanged);
event 仅有发布类可以触发事件
publicdelegatevoidNumberChangedEventHandler(object sender,EventArgs eventArgs);
publicclassNumberChangedEventArgs : EventArgs{
int m_Number;
public NumberChangedEventArgs(int number){
m_Number = number;}
publicint Number{
get { return m_Number; }
}
}
publicclassMyPublisher{
publiceventNumberChangedEventHandler NumberChanged;
publicvoid FireNewNumberEvent(EventArgs eventArgs){
try{
if (NumberChanged != null){
NumberChanged(this, eventArgs);
}}
catch { }} } publicclassMySubscriber{ publicvoid OnNumberChanged(object sender, EventArgs eventArgs){ NumberChangedEventArgs numberArg = eventArgs asNumberChangedEventArgs; Debug.Assert(numberArg != null); string message = numberArg.Number.ToString(); MessageBox.Show("The new number is " + message); } } MyPublisher publisher = newMyPublisher(); MySubscriber subscriber = newMySubscriber(); publisher.NumberChanged += subscriber.OnNumberChanged; NumberChangedEventArgs numberArg = newNumberChangedEventArgs(4); publisher.FireNewNumberEvent(numberArg); 范型事件
publicdelegatevoidGenericEventHandler(T t,U u);
publicclassMyArgs : EventArgs { }
publicclassMyPublisher{
publiceventGenericEventHandler MyEvent;
publicvoid FireEvent(){
MyArgs args = newMyArgs();
MyEvent(this,args);
}
}
publicclassMySubscriber where A : EventArgs{
publicvoid OnEvent(MyPublisher sender, A args) { }
}
MyPublisher publisher = newMyPublisher();
MySubscriber subscriber = newMySubscriber();
publisher.MyEvent += subscriber.OnEvent;
使用别名
usingMyHandler = GenericEventHandler;
publiceventMyHandler MyEvent;
类型安全的EventsHelper
usingMyEventHandler = GenericEventHandler;
publicdelegatevoidGenericEventHandler();
publicdelegatevoidGenericEventHandler(T t,U u);
publicclassMyPublisher{
publiceventMyEventHandler MyEvent;
publicvoid FireEvent(int number, string str){
EventsHelper.Fire(MyEvent,number,str);
}
}
publicstaticclassEventsHelper{
publicstaticvoid UnsafeFire(Delegate del,paramsobject[] args){
if (del == null){
return;
}
Delegate[] delegates = del.GetInvocationList();
foreach (Delegate sink in delegates){
try{
sink.DynamicInvoke(args);
}
catch { }
}
}
publicstaticvoid Fire(GenericEventHandler del,T t,U u)
{
UnsafeFire(del,t,u);
}
publicstaticvoid Fire(GenericEventHandler del, T t)
{
UnsafeFire(del, t);
}
}
事件访问器
usingMyEventHandler = GenericEventHandler;
publicdelegatevoidGenericEventHandler();
publicdelegatevoidGenericEventHandler(T t);
publicdelegatevoidGenericEventHandler(T t,U u);
publicclassMyPublisher{
MyEventHandler m_MyEvent; publiceventMyEventHandler MyEvent{ add { m_MyEvent += value; } remove { m_MyEvent -= value; } } publicvoid FireEvent(){ EventsHelper.Fire(m_MyEvent,"Hello"); } } publicclassMySubscriber{ publicvoid OnEvent(string message){ MessageBox.Show(message); } } MyPublisher publisher = newMyPublisher(); MySubscriber subscriber = newMySubscriber(); publisher.MyEvent += subscriber.OnEvent; publisher.FireEvent(); publisher.MyEvent -= subscriber.OnEvent; 第七章 异步调用
对象在后台执行方法调用后,控制权立即返回客户端,随后能以某种方式通知客户端该方法已经执行完毕
面向组件的异步调用机制必备条件
1. 即可以用于同步也可以用于异步
2. 由客户端来决定是同步还是异步
3. 客户端能够发起多个异步调用
4. 组件能够服务于多个并发调用
5. 组件的方法有输出参数或是返回值,客户端应该有办法在方法结束时获取他们
6. 发生在组件端的错误能反馈到客户端
7. 异步调用机制方便使用
客户端执行一个异步方法后有以下选择
1. 阻塞直到调用完成
2. 等待方法调用完成
3. 等待一段时间,停止等待
4. 同时等待多个方法的完成
5. 回调函数
publicdelegateintBinaryOperation(int argument1, int argument2);
publicclassCalculator
{
publicint Add(int argument1,int argument2)
{
return argument1 + argument2;
}
publicint Subtract(int argument1, int argument2)
{
return argument1 - argument2;
}
}
Calculator calculator = newCalculator();
BinaryOperation oppDel = calculator.Add;
//oppDel(2,3);
//oppDel.Invoke(2,3);
IAsyncResult asyncResult = oppDel.BeginInvoke(2,3,null,null);
int result = oppDel.EndInvoke(asyncResult);
EndInvoke()在每次异步操作时只能调用一次。
委托类的内部列表中只能允许有一个目标方法
只能在分发调用的那个委托对象上把IAsyncResult对象传递给EndInvoke()
publicclassCalculatorClient
{
IAsyncResult m_AsyncResult;
publicvoid AscuncAdd()
{
Calculator calculator = newCalculator();
DispatchAdd(calculator,2,3);
int result = GetResult();
}
int GetResult()
{
AsyncResult asyncResult = (AsyncResult)m_AsyncResult;
BinaryOperation oppDel = (BinaryOperation)asyncResult.AsyncDelegate;
Debug.Assert(asyncResult.EndInvokeCalled == false);
return oppDel.EndInvoke(m_AsyncResult);
}
void DispatchAdd(Calculator calculator, int argument1, int argument2)
{
BinaryOperation oppDel = calculator.Add;
m_AsyncResult = oppDel.BeginInvoke(argument1, argument2, null, null); } } 阻塞 IAsyncResult asyncResult = oppDel.BeginInvoke(2,3,null,null); while (asyncResult.IsCompleted == false) { asyncResult.AsyncWaitHandle.WaitOne(10,false); } int result = oppDel.EndInvoke(asyncResult); BinaryOperation oppDel1 = calculator.Add; BinaryOperation oppDel2 = calculator.Add; IAsyncResult asyncResult1 = oppDel1.BeginInvoke(2, 3, null, null); IAsyncResult asyncResult2 = oppDel2.BeginInvoke(2, 3, null, null); WaitHandle[] handleArray = { asyncResult1 .AsyncWaitHandle,asyncResult2.AsyncWaitHandle}; WaitHandle.WaitAll(handleArray); 回调方法
publicclassCalculatorClient
{
publicvoid AscuncAdd()
{
Calculator calculator = newCalculator();
BinaryOperation oppDel = calculator.Add;
oppDel.BeginInvoke(2,3,OnMethodCompletion,null);
}
void OnMethodCompletion(IAsyncResult asyncResult)
{
int result = 0;
AsyncResult resultObj = (AsyncResult)asyncResult;
Debug.Assert(resultObj.EndInvokeCalled == false);
BinaryOperation oppDel = (BinaryOperation)resultObj.AsyncDelegate;
result = oppDel.EndInvoke(asyncResult);
Trace.WriteLine("aa" + result);
}
}
参数传递
int asyncsState = 4;
oppDel.BeginInvoke(2, 3, OnMethodCompletion, asyncsState);
int ret = (int)asyncResult.AsyncState;
异步流
异步web
异步错误处理
try
{
result = oppDel.EndInvoke(asyncResult);
Trace.WriteLine("aa" + result);
}
catch { }
异步事件
异步调用的缺陷:
1. 线程并发与同步
2. 线程池的消耗
3. 对引用和引用类型的过早访问
4. 费时的构造函数
5. EndInvoke()后的清理
第八章 多线程和并发管理
一个线程仅仅是一个进程中的一条执行路径
面向cpu的操作用计算机的中央处理器进行密集的或重复的操作
面向I/O 的操作是使用输入或输出设备
创建线程
publicclassMyClass
{
publicvoid ShowMessage()
{
Thread currentThread = Thread.CurrentThread;
string caption = "Thread ID = ";
caption += currentThread.ManagedThreadId;
MessageBox.Show(caption);
}
}
MyClass obj = newMyClass();
//ThreadStart threadStart = obj.ShowMessage;
//Thread workerThread = new Thread(threadStart); Thread workerThread = newThread(obj.ShowMessage); workerThread.Start(); Thread.Sleep(0);强制上下文切换
自动同步
如果一组面向上下文的对象共有一把锁,就说他们在一个同步域中
一个对象可以处于以下任何一个同步域中:
1.在它创建者的同步域中(该对象和它的创建者共享一把锁)
2.在新的同步域中(对象有自己的锁并启动一个新的同步域)
3.完全不在同步域中(因为没有锁,并发访问是允许的)
SynchronizationAttribute.REQUIRED 创建者同步域
SynchronizationAttribute.REQUIRES_NEW 新同步域 类工厂应用
同步域的缺陷:
1. 只能在面向上下问的对象上使用,对于.net的其他类型,仍然手动同步
2. 以代理和拦截器的方式访问面向上下文的对象要付出一定的代价
3. 不能保护静态类成员和静态方法
4. 不是一个面向吞吐量的机制
手动同步
监视器
只和引用类型一块工作的锁
MyClass obj;
Monitor.Enter(obj);
Try
{
obj.ShowMessage();
}
Finnaly
{
Monitor.Exit(obj);
}
保护静态成员和方法
publicclassMyClass
{
staticpublicvoid ShowMessage()
{
}
}
Monitor.Enter(typeof(MyClass));
MyClass.ShowMessage();
Monitor.Exit(typeof(MyClass));
MyClass obj;
lock (obj){obj.ShowMessage();}
publicclassMyClass
{
publicvoid ShowMessage()
{
lock (this)
{
}
}
}
属性不需要加临时变量
publicclassMyClass
{
int m_Number;
publicint Number
{
get { lock(this) { return m_Number; } } } } 同步方法
[MethodImpl(MethodImplOptions.Synchronized)]
publicvoid DoSomething()
{
}
可等待句柄
与Monitor的区别:
Monitor只能在引用类型上使用
可等待句柄同步对值类和引用类型进行访问
可等待句柄多个对象等待是一个原子操作
互斥
命名Mutex过去产物
一个命名Mutex的技巧:
不一定要同步对系统范围资源的访问,而是简单易用的跨进程通信机制,为单例应用程序提供了方便
可等待事件
与Monitor区别:
Monitor受到束缚后没有该动作的任何记忆
事件有记忆,状态保存在信号已发上
信号
Mutex 也叫 二进制 Semaphores
以某种可计算的方式控制线程的执行
互锁
只是增加减少数值或交换比较数值
支持一系列以原子方式访问变量的静态方法
读/写锁
使用Monitor对象锁定整个对象的效率很差,一个宏观的锁会锁定所有对象
使用Mutex效率也很差,如果没有线程写入新数值,多个线程就可以并发的读取当前数据
一个成员变量或属性。经常被多线程读写
同步委托
publiceventEventHandler MyEvent;
publicvoid FireEvent()
{
FireEvent();
}
[MethodImpl(MethodImplOptions.NoInlining)]
void FireEvent(EventHandler handler)
{
if(handler != null)
{
handler(this,EventArgs.Empty);
}
}
使用.net多线程服务
线程相关静态变量
线程本地存储
命名内存槽
未命名内存槽
线程池
工作线程用于处理异步执行或计时器 25
完成端口线程是用来与服务器操作联合的 1000
计时器
第九章 序列化和持久化 序列化当应用程序关闭时,对象状态被保存在文件中
反序列化应用程序启动时加载一些包含数据的文件,并将文件中的信息转化为对象
自动序列化 .net通过反射实现对象的自动序列化每个.net类型的状态大批能够通过反射来读取或设置 .net把对象状态序列化为流
.net序列化是基于对象的只有实例字段被序列化静态字段不行
枚举永远是可序列化的
[Serializable]
publicclassMyClass
[NonSerialized]
当.net 反序列化时,它会将每个不可序列化成员变量初始化为该类型的默认值
[Serializable]
publicclassMyClass :IDeserializationCallback
{
[NonSerialized]
IDbConnection m_Connection;
string m_ConnectionString;
publicvoid OnDeserialization(object sender)
{
m_Connection = newSqlConnection();
m_Connection.ConnectionString = m_ConnectionString;
m_Connection.Open();
}
}
所有委托定义可序列化类
.net带有两个格式器
二进制格式器和soap格式器
自定义序列化
第十章 远程处理
内部网环境下.net remoting
Internet web服务
在托管代码和非托管代码之间的桥梁
应用程序域是建立在非托管进程上的
同一进程的所有应用程序域共享同一托管堆
.net把包含服务器对象的应用程序域称为宿主应用程序域
值传递 引用传递
引用封送激活模式 客户端激活和服务器激活
客户端激活对象
服务端single-call
服务端singleton
客户端代理 透明代理 真实代理
当使用http作为传输协议时,可以选择用SOAP来穿过防火墙
三种协议:
1. tcp
2. http
3. ipc 只能在同一机器的跨进程调用使用
编程式和管理式
第十一章 上下文与拦截
拦截:拦截从客户端到对象的调用,执行某些调用前处理,将调用转发给对象,执行某些调用后处理,然后将控制返回给客户端
上下文敏捷对象
面向上下文对象
组件服务类型
面向上下文服务和企业服务
第十二章 安全
附录A 基于接口的Web服务
附录B 统一windows forms 和asp.net安全
附录C 反射与属性
附录D 范型
附录E c#编码准则