祝大家圣诞节快乐!有事没事别出门,外面太!挤!了!
此文是《.NET:框架设计原则、规范》的读书笔记,本文内容较多,共分九章,今天推送最后一章。
1. 什么是好的框架
2. 框架设计原则
3. 命名规范
4. 类型设计规范
5. 成员设计规范
6. 扩展性设计
7. 异常
8. 使用规范
9. 设计模式
Aggregate Component:
把多个底层类型集中到一个高层类型中,以此来支持常用场景。例如E-mail组件、System.Net.WebClient、System.Messaging.MessageQueue、System.IO.SeralPort、System.Diagnostics.EventLog
component-oriented design:
通过类型来暴露API,而类型由函数、属性、方法及事件组成。
其使用模式为:Create-Set-Call
注意不要让对象处于不可用的状态,或者对方法的调用有先后顺序依赖
A. 应该有默认构造函数
B. 构造函数的所有参数应该与属性相对应,并用来对属性进行初始化
C. 大多数属性应该有getter和setter
D.所有属性都有合理的默认值
E. 如果参数在主要场景的方法调用之间不会改变,那么方法就不应该带这样的参数。这样的选项应该通过属性来指定。
F. 方法不以委托为参数。所有回调函数都通过事件来实现。
组成聚合类型的子类型,称为因子类型(factoredtype)
A. 没有状态
B. 有非常清晰的生命周期
C. 可用通过聚合组件的属性或方法访问
D.用于高级场景或与系统的不同部分集成
异步API建模:
一个是“经典的”,一个是“基于事件的”
经典模式使用回调函数,在任意线程中执行。更加灵活强大,性能也更高。
基于事件模式使用事件,更容易学习。可以和VisualStudio集成。
public class APMTestRun1
{
publicstatic void AsyncRun()
{
Utility.Log(""APMAsyncRun:start"");
stringurl = ""http://sports.163.com/nba/"";
HttpWebRequestwebRequest =HttpWebRequest.Create(url) as HttpWebRequest; // webRequest是一个 IAsyncResult
webRequest.BeginGetResponse(Callback,webRequest);//开始异步操作,设置回调Callback,此Callback函数会在另外一个线程中运行,在任务完成的时候调用
Utility.Log(""AsyncRun:download_start"");
}
// 回调函数,参数ar用来执行EndGetResponse()
privatestatic void Callback(IAsyncResult ar)
{
varsource = ar.AsyncState as HttpWebRequest;
varresponse = source.EndGetResponse(ar); //一直阻塞,直到结束异步操作。实际上在这里一般都可以立刻返回,主要是为了清理掉异步状态防止内存漏洞
using(var stream = response.GetResponseStream())
{
using(var reader = new StreamReader(stream))
{
stringcontent = reader.ReadToEnd();
Utility.Log(""AsyncRun:result_size=""+ Utility.GetStrLen(content));
}
}
}
}
方法签名标准:
// 同步方法
public <return>Operation(<parameters>, <out params>)
// 异步方法
public IAsyncResultBeginOperation(<parameters>, AsyncCallback callback, object state)
public <return>EndOperation(IAsyncResult asyncResult, <out params>)
ii. 要确保begin方法的返回类型实现了IAsyncResult接口
//一个菲波拉契数列的生成器,以异步方式调用
public class FiboCalculator {
delegatevoid Callback(int count,ICollection<decimal> series); //关于异步线程的原型声明,按业务需求设置
privateCallback callback = new Callback(GetFibo); //生成异步处理线程对象,注意GetFibo和Callback是两个类型,GetFibo表示一个业务功能
//开始生成一个序列的过程,然后返回
//返回值是由线程启动方法BeginInvoke所产生的,用来表示一次异步过程
publicIAsyncResult BeginGetFibo{
intcount,
ICollection<decimal>series,
AsyncCallback callback,
object state)
{
returnthis.callback.BeginInvoke(count, series, callback, state); //BeginInvoke()的参数列表前半段为异步业务函数的参数,后2个为线程处理所需的参数:callback->任务结束后的回调;state->传递给callback函数的参数
}
//阻塞,直到生成序列的过程完成。
// 用户可以在主线程中调用此方法阻塞直到返回,也可以放在异步回调方法里面,用来清理异步调用的内存漏洞。
publicvoid EndGetFibo(IAsyncResult asyncResult) {
this.callback.EndInvoke(asyncResult);//EndInvoke()的作用为阻塞直到callback线程退出,参数应该为BeginInvoke()的返回值
}
//生成一个第一个参数count的数量的菲波拉契数列数列,此方法可能会造成一段时间的阻塞,因为可能需要很多层递归
publicstatic void GetFibo(
intcount, ICollection<decimal> series)
{
for(int i = 0; i < count; i++) {
decimald = GetFiboCore(i);
lock(series) {
series.Add(d);
}
}
}
//返回第N个菲波拉契数
staticdecimal getFiboCore(int n) {
if(n < 0) throw new ArgumentException(""n must be >0"");
if(n == 0 || n == 1) return 1;
returnGetFiboCore(n-1) + getFiboCore(n-2);
}
}
userState参数用来区别对于同一个异步方法的多次调用:
public void MethodAsync(stringarg1, string arg2, object userState);
这个userState会传递到事件处理函数里,用来让事件处理函数分辨是哪一次异步请求所产生的事件。
增加一个额外的ProgessChanged事件,这个事件由异步操作引发。
传给此事件的处理程序的事件参数:ProgressChangedEventArgs参数中有一个表示进度的属性,该属性为0-100。
A. 要确保如果在一个异步操作中实现了ProgressChanged事件,那么在操作的完成事件被触发之后,不应该再出现此类事件。
在少数情况下,异步操作可以在操作完成之前不定期的返回增量结果(incrementalresult)。
Dependency Properties
依赖属性的值不保存在类型的字段中,而是放在一个属性存储区中。比如对象和对象容器的关系;Panel容器中的一个Button对象。
拿Button来讲,它的继承树是Button->ButtonBase->ContentControl->Control->FrameworkElement->UIElement->Visual->DependencyObject->…
每次继承,父类的私有字段都被继承下来。当然,这个继承是有意思的,不过以Button来说,大多数属性并没有被修改,仍然保持着父类定义时的默认值。通常情况,在整个Button对象的生命周期里,也只有少部分属性被修改,大多数属性一直保持着初始值。每个字段,都需要占用4K等不等的内存,这里,就出现了期望可以优化的地方:
因继承而带来的对象膨胀。每次继承,父类的字段都被继承,这样,继承树的低端对象不可避免的膨胀。
大多数字段并没有被修改,一直保持着构造时的默认值,可否把这些字段从对象中剥离开来,减少对象的体积。
参见: IDisposable
手动释放非托管资源的方法:
调用.Dispose()方法
使用using(DisposableObjectobj = new DisposableObject()){ ... }
在资源类的析构函数写释放代码,但是无法确定什么时候被释放
参见: 要为所有的可终结类型实现“基本Dispose模式”
基本Dispose模式的一个简单实现:
public classDisposableResourceHolder : IDisposable {
privateSafeHandle resource; // 掌握一个资源
publicDisposableResourceHolder() {
this.resource= ... // 分配资源
}
publicvoid Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
//此方法被IDisposable.Dispose方法所调用时disposing 为 true,所以应该检查资源是否还可用
//此方法被终结器(垃圾回收机制)调用时 disposing 为false。
protectedvirtual void Dispose(bool disposing) {
if(disposing) {
if(resource != null) resource.Dispose();
}
}
}
如果类型覆盖了终结方法(析构函数),并在Dispose(bool)中加入支持终结的代码,以此来扩展基本Dispose模式,那么这些类型就是可终结类型。
这种是把非托管资源封装成托管资源的做法。性能不高
参见: 基本Dispose模式
如果从终结方法抛出异常,那么CLR会关闭整个进程。
所谓转换风格:
int i =int.Parse(""35"");
DateTime d =DateTime.Parse(""10/10/1999"");
语言集成查询:Language-IntegratedQuery
抽象的一部分实现支持某种特性,而其他实现则不支持该特性。如stream的实现可能会支持读、写、定位或其他组合。
// 可选功能在继承的时候按所提供的范围来覆盖
public abstract class Stream {
publicabstract void Close();
publicabstract int Position { get; }
//可选的写入功能
publicvirtual bool CanWrite { get { return false; } }
publicvirtual void Write(byte[] bytes) {
thrownew NotSupportedException(...);
}
//可选的搜索功能
publicvirtual bool CanSeek { get {return false;} }
publicvirtual void Seek(int position) {
thrownew NotSupportedException(...);
}
//其他可选功能
}
泛型生成的类因为没有一个公共的基类,在某些情况下很不好操作。比如List<T>就不是List<object>的子类,在需要管理多个List的时候会很麻烦。
因此,我们需要用SimulatedCovariance模式:
最常见的形式由一个过着多过非虚(通常是公有)成员组成,这些成员通过调用一个或者多个受保护的虚成员来实现。
目标是对扩展性加以控制。
public void SetBounds(...) {
...
SetBoundsCore(...);
}
protected virtual voidSetBoundsCore(...) {...}
需要支持超时的API的设计规范
用方法的参数比属性好,这使操作与超市长度之间的关系更加明确。
用整数来表示超时长度有几个缺点:
如果要用整数,需要满足:
XAML是WPF用来表示对象图的一种XML格式,一般用于画UI
感谢大家的阅读,如觉得此文对你有那么一丁点的作用,麻烦动动手指转发或分享至朋友圈。如有不同意见,欢迎后台留言探讨。