Programming .Net component 2nd (部分中文翻译)

第一章
局部类
C#允许我们通过关键字partial将类或结构的定义、实现分放在不同的文件中:
public partial class MyClass
  {//some code }
public partial class MyClass
{//another part of this class}
一个类(或结构)具有两个方面的性质:可积聚的和不可积聚的。
可积聚的:类或结构的每一个部分都允许的,诸如:添加属性、索引器、成员、接口继承、方法等。
不可聚积的:所有部分都必须保持一致的,诸如:基类、访问限制、时类还是个结构类数据等。

全局命名空间
默认情况下,所有的C#命名空间都嵌套在global命名空间内。无论你通过using引用、还是使用全名使用一个类,C#都隐式的在目前嵌套命名空间内开始名称搜索方案,你可以通过标示符::将其改为从global开始。显示搜索方案在解决命名冲突很有帮助。
namespace MyNamespace{
       namespace System{
          class MyClass{
             public void MyMethod(  ){
              global::System.Diagnostics.Trace.WriteLine("It Works!");
             }
          }
       }
}
该例中,如果不使用“global::”,命名搜索回由MyNamespace开始,并得到我们定义的System而非.NET框架提供的System。

为引用设置别名
默认情况下,所有的C#命名空间都嵌套在global命名空间内。当为程序集设定一个别名后,该程序集中的命名空间就不再属于global,而是基于设定的别名,也就时说,在解决命名空间冲突时,你可以使用:
MyAliases::System.Diagnostics.Trace.WriteLine("It Works!");
而不是:
global::System.Diagnostics.Trace.WriteLine("It Works!");
例:如果你为MyLibrary.dll程序集取别名MyLibraryAlias,则使用时有:
    //先在using指示前,使用extern alias 指示
extern alias MyLibraryAlias;
     
    MyLibraryAlias::MyNamespace.MyClass obj;
    obj = new MyLibraryAlias::MyNamespace.MyClass(  );
其中extern alias 指示:通知编译器在搜索路径包含别名内的类型。
用别名+命名空间全部修饰符访问数据过于长,可以使用using为其全部修饰符取别名:
using MyLibrary = MyLibraryAlias::MyNamespace;
MyLibrary.MyClass obj;

vshost.exe
在VS 2005环境下,编译一个应用程序,VS 2005会自动在该应用程序的同一文件夹下创建一个名为<application name>.vshost.exe的程序集。当你调试应用程序时,VS 2005环境实际上启动的是:
<application name>.vshost.exe
而非应用程序本身。vshost.exe是个仅仅包含一个Main()的简单应用程序集。该Main方法连接了.NET hosting的管理类集,这些类具有如下特性:
Partial-trust debugging
This enables you to test how your application behaves under reduced security permissions.
Shorter startup time
In Visual Studio 2005 the hosting process is kept running between debug sessions, significantly shortening startup time.
Design-time expression evaluation
The Intermediate Window lets you test code from your application without launching it. This is done by running the code in the readily available, already running <application name>.vshost.exe file.
Note that you can only run <application name>.vshost.exe from within the debugger, and it only works when placed in the same folder as the application it hosts.

客户端和服务器程序集
在VS 2005环境下,EXE文件也可以被引用,作为一个服务端程序集。但EXE文件由几个特性是DLL程序集所不能替代的:
仅有EXE程序集可以直接启动;
仅有EXE可以决定CLR运行的版本;
Partial-trust debugging in VS 2005仅在EXE中使用;
VS 2005中,点击完成分布、部署策略仅在EXE中有效。

管理组件的可视化
.NET有两种类型的组件,它们由public和internal标识。Internal组件仅可以被在同一个程序集中的客户端所访问,组建默认是作为一个internal组件。
有些组件允许其成员只能被内部(程序集内部)客户端访问,除非该组件的继承类,其可以在其他程序集中访问该方法。
public class MyClass{
    public MyClass(  ){}
public string GetMessage(  )
{ return DoWork(  ); }
protected internal string DoWork(  )
    { return "Hello";}
}
该例中,DoWork若以MyClass类成员身份出现,则只能在程序集内部被访问;但MyClass子类(不论是在内部还是外部)可以调用此方法。

程序集清单(assembly-level attribute的使用)
命名空间System.Runtime.CompilerServices 和System.Reflection提供了一些属性,你可以通过这些属性向程序集清单中添加相关的信息。通常的提供的信息包括身份确定、安全策略。当然,你可以将这些信息放在程序集各个源文件中,但通常的我们都将其放在AssemblyInfo.cs文件中。
例:
using System.Reflection;
using System.Rue.CompilerServices;

    [assembly: AssemblyTitle("MyAssembly")]
    [assembly: AssemblyDescription("Assembly containing my .NET components")]
    [assembly: AssemblyCompany("My Company")]
    [assembly: AssemblyCopyright("Copyright © My Company 2005")]
    [assembly: AssemblyTrademark("MyTrademark")]
    [assembly: AssemblyVersion("1.2.3.4")]

 
Friend程序集
.NET 2.0在命名空间System.Runtime.CompilerServices 为assembly-level attribute添加了一属性InternalsVisibleTo,该属性允许暴露内部程序集成员。例:
internal class MyInternalClass
    {
       public void MyPublicMethod(  ){...}
       internal void MyInternalMethod(  ){...}
    }
若在AssemblyInfo.cs文件中添加如下带码:
[assembly: InternalsVisibleTo("MyClient")]
则在MyClient.dll 和 MyClient.exe中的代码就可以任意的访问MyInternalClass类的public或internal方法。

第二章  基于接口的程序
定义接口:接口和类一样,具有public和internal限制,其默认为public。而类的方法、成员是恒public的,其在实现时,也必须标志为public。
使用接口的唯一途径:实例化一个支持该接口的类,并将其赋值给接口变量。
接口促进了客户端和对象间的松藕合结构,客户端代码和实现接口对象间必须注意……

显式接口实现
显式实现的方法只能通过接口访问,而不能通过实现该接口的类访问。在现式实现时,代码不能带有任何可视化修师符。例:
public class MyClass : IMyInterface
    {  void IMyInterface.Method1(  ) {...}
       void IMyInterface.Method2(  ) {...}
       //Other methods and members
    }
其客户端代码:
     IMyInterface iA;
       iA = new MyClass(  );
       iA.Method1(  );
建议,最好别将显式实现和隐式实现混合在一起。

接口和类型安全
为使用接口,必须先实例化其实现类的一实例,然后将对象转换为接口引用类变量。将对象转换为接口引用变量有两种方式:显式转换和隐式转换。
隐式转换:将一类实例赋值给一相应的接口变量即称隐式转换。当使用隐式转换时,编译器要求类型时安全的。如果该类没有实现这个接口,编译器将不能正确编译。
显式转换:编译器不会验证其类型是否安全,编译器将始终生成代码,而不全转换是否安全。如果类型转换失败,将会在程序运行时抛出错误。
隐式转换无效两种情况:
1, 使用非泛类工厂时
类工厂:在面向对象程序中,客户端在创建对象时,不是直接的实例化该类的一个对象,而是向类工厂请求实例化一对象;类工厂在接收到请求时,创建相应的类对象实例。
使用类工厂的优点是仅有类工厂同那些支持接口的实际组件类向相关类,而客户代码仅仅是知道存在接口。当你欲转换一服务,你只需要修改类工厂,而客户代吗不受影响。
而类工厂返回的则是一般的基类(通常为object),这是可以使用显式转换:
 public interface IClassFactory
   {  object GetObject(  );}
  
    IClassFactory factory;
    /* Some code to initialize the class factory */
    IMyInterface obj;
    obj = (IMyInterface)factory.GetObject(  );
    obj.Method1(  );
例中,接口变量obj引用的是我们需要类的基类对象实例。即,在通过基类使用接口技术时(ImyInterface基类并未实现该接口),必须显式转换。
2, 当接口变量引用其实现类所实现的其他接口的变量时,使用现时转换(隐式转换无效)。例:
public interface IMyInterface
    { void Method1(  );  void Method2(  );}
public interface IMyOtherInterface
    { void Method3(  );}
   
public class MyClass : IMyInterface,IMyOtherInterface{
       public void Method1(  ){...}
       public void Method2(  ){...}
       public void Method3(  ){...}
    }
 //Client-side code:
    IMyInterface obj1;
    IMyOtherInterface obj2;
     
    obj1 = new MyClass(  );
    obj1.Method1(  );
 // 通过接口变量obj1取得其实现类的引用,此时必须使用显式转换
    obj2 = (IMyOtherInterface)obj1;
    obj2.Method3(  );
 不论在何种情况下,使用显式转换都建议使用error handing(try …catch/finally)。
当然,可以使用as操作符,以避免类型安全问题。

接口和结构
尽管结构不能有基类或基结构,当其可以继承自接口。在需要抽象数据存储(结构)时,我们可以通过接口来实现。

实现多重接口
当一个类实现多个接口时,如果这些接口中有相同的方法声明时,我们可以选择:所有接口具有相同的实现,和显式实现不同的接口两种方式。而若用显式实现,同时,在一个接口的实现中欲引用另一接口的实现方法时,必须使用this 查询所引用的接口。例:
public class MyClass : IMyInterface, IMOtherInterface{
      void IMyInterface.Method1 ( ){...}
      void IMyOtherInterface.Method1 ( ){
        IMyInterface myInterface = this;
        myInterface.Method ( );
      }
    }
this关键字的这种使用是在的接口实现类中调用显式实现方法的唯一方法。

接口和类继承
在派生类方法中,可以使用new引用其基类的方法:
public class BaseClass
     { public virtual void TraceSelf(  ){ … }} 
    public class SubClass : BaseClass
     { public new void TraceSelf(  ){ ...} }
//在这通过派生类调用的TranceSelf是基类中的方法。
不论基类方法是否声明为virtual,如果在派生类使用了new(如上),其调用的方法都是基类方法,而非派生类定义的方法。
但在实现了接口的类中使用:
public class A : ITrace{
       public virtual void TraceSelf(  )//virtual is optional 
       {  Trace.WriteLine("A"); }
}

public class B : A{
       public new void TraceSelf(  )
{ Trace.WriteLine("B"); }
 }
其中:
ITrace obj  = new B(  );
        obj.TraceSelf(  );   //Trace A

A obj = new B(  );
        obj.TraceSelf(  );//Traces "A"

但如果该类显式地实现类一接口的方法,要在其派生类实现如上调用,则必须使用base。例:
   public class A : ITrace{
       protected virtual void TraceSelf(  ){  … }
       void ITrace.TraceSelf(  ) { TraceSelf(  );}
}

    public class B : A{
       protected override void TraceSelf(  )
       { Trace.WriteLine("B");   base.TraceSelf(  ); }
}

第三章  接口和泛类型
使用了泛类型的接口同一般的接口有相同的功能,其主要不同在于继承泛接口时,必须提供一种类型(该类型也可以同样为泛类型)替代泛类型。

泛接口的继承
当从泛接口中派生新接口时,该派生接口通常也作为一个泛类接口出现:
public interface ISubInterface<T> : IBaseInterface<T>
当然我们也可以具体化该接口:
public interface ISubInterface<T> : IBaseInterface<string>
同样,实现泛类时,我们也可以定义一个泛类,和具体实现。:
public class List<T> : IList<T>{
       public void AddHead(T item){...}
       //Rest of the implementation
    }

public class List<T> : IList<T>{ …….}

显式实现泛接口
当用不同的参数类型多次实现泛接口式,如果使用隐式转换我们可能遇到如下问题:
方法签名中不包含泛类型:
public class NumberList : IList<int> {
            void IList<int>.RemoveAll(  ) {...}
          }
RemoveAll在Ilist中的生命为:
Void  RemoveAll();
如果不使用显式转换,则不论是string、int还是其他具体的链表,其RemoveAll全都相同。
返回值为返回类型时,我们不能通过仅仅是返回值不同类型而重载函数。对形如:
public interface IList<T>
        {  T GetHead(  ); }
我们使用隐式转换总是难以将各个具体链表的GetHead方法区分开。

泛类型中的操作
并不是所有的类都包含+、-、+=、-=等操作符,在泛实现泛接口时,我们并不知道具体的类是否要实现这些操作。通常我们通过两个方法解决这个问题。一是,不使用泛类实现接口;二,使用约束。
虽然我们可以在泛接口中使用约束,但我们一般都在其泛实现类上定义约束。因为接口是抽象的,不提供任何的实现,而这约束却也算是中实现。若在接口上定义约束,这也就违背了其本质含义。

类的泛约束
泛类可以限制其泛类的范围:
public class ListClient<L,T> where L : IList<T>
泛类型变量仅允许隐式转换为object或其约束条件下的对象,而对一个泛类对象使用显式转换一般都是比较危险的。例:
public interface IsomeInterface
{...}
public class SomeClass
{...}
public class MyClass<T> {
void SomeMethod(T t)
{
         ISomeInterface obj1 = (ISomeInterface)t;//Compiles
         SomeClass      obj2 = (SomeClass)t;     //Does not compile
       }
 }
该例中,obj2位一个类,而obj1位一接口。编译器允许我们显式的将一个泛类型参数转化为一个接口变量,而不允许我们将其转换为一类变量。为此,当需要显式转换时,我们通常使用的是is、as操作符。

泛方法
无论接口本身是否为泛类接口,其方法成员的参数可以声明为泛类型。当然如果接口非泛类的,就不能声明泛类的返回值。因为返回值不作为方法签名的一部分。
泛类方法样例:
public interface ImyInterface {
       void MyMethod<T>(T t);
}
其实现类:
public class MyClass : IMyInterface{
public void MyMethod<T>(T t) {...}
}

接口设计与分解
    接口是逻辑方法和属性的叙述,而组成的逻辑方法和属性的通长都是域类级的。
接口分解:我们可以想象有几个接口是同一实体的各个不同方面特性。当我们确定该实体的所有方法和属性,我们必须将其指派给相应的接口。
例如设计一个Dog类,包含:Bark、Fetch两个方法;以及兽医诊所注册号、是否看过兽医两个。若这些类成员全部都继承自接口Idog,此时,我们称这个接口不具有好的分解。因为不是只有Dog具有这样的两个属性,其它的类(宠物)也具有。为此我们可以设计接口:Idog、Ipet,将两个方法指派Idog,而两属性指派Ipet,这个过程我们程其为接口分解。
一个好的接口一般只包含:3-5个成员,较多的也就6-9个成员。


第四章  受管堆
.NET不由底层操作系统分配内存,在系统的每个包含.NET的进程中,.NET运行时预先分配一个成为受管堆的堆。受管堆:在每创建一个对象时,受管堆为该对象分配内存。受管堆是一链型内存对象,其中包含指针,该指针指向下一个有效的内存块。当.NET需要创建一个新对象时,受管堆将为其分配内存空间,并改变它自己的指针字段,一指向下一个有效的可分配内存空间。

传统的内存分配机制
传统分配内存:直接操作内存块,是故,在每创建一个对象都必须预先设计好其析构函数。
COM的引用计数:扩大了对象的使用范围。

垃圾回收器
In the abstract, when the JIT compiler compiles the IL code, it updates a list of roots top-level primordial application starting points, such as static variables and methods (Main, for example), but also internal .NET entities that should be kept alive as long as the application is running. Each root forms the topmost node in a tree-like graph.
.NER跟踪受管堆分配的每一个新对象,以及该对象和它的客户端的联系。当一个对象被分派,.NET更新它的对象图并向图中添加一指向创建该对象的引用。
类似的,.NET在客户端接收到一某对象的引用和将对象复制为另一对象成员时也要更新其图。
The entity responsible for releasing unused memory is called the garbage collector. When garbage collection is triggered (usually when the managed heap is exhausted, but also when garbage collection is explicitly requested by the code), the garbage collector deems every object in the graphs as garbage. The garbage collector then recursively traverses each graph, going down from the roots, looking for reachable objects. Every time the garbage collector visits an object, it tags it as reachable. Because the graphs represent the relationships between clients and objects, when the garbage collector is done traversing the graphs, it knows which objects were reachable and which were not. Reachable objects should be kept alive. Unreachable objects are considered garbage, and therefore destroying them does no harm. This algorithm handles cyclic references between objects as well (i.e., where Object A references Object B, which references Object C, which references back to Object A). When the garbage collector reaches an object already marked as reachable, it doesn't continue to look for other objects reachable from that tagged object.
Next, the garbage collector scans the managed heap and disposes of the unreachable objects by compacting the heap and overwriting the unreachable objects with reachable one. The garbage collector moves reachable objects down the heap, writing over the garbage, and thus frees more space at the end for new object allocations. All unreachable objects are purged from the graphs.

Object Finalization
.NET 对象并不被告知它们什么时候变为垃圾,而是在受管堆受限或是显式地告知时,它们才作为垃圾,进而被回收。
当一对象包含资源时,诸如文件、连接等,我们并不能通过垃圾回寿器自动释放这些资源,为此,.NET提供了Finalization技术:
protected void Finalize(  );
如果对象必须作某些专门的清理工作,我们就必须实现一个称为Finalize()的方法。

finalization queue:
如果对象实现了Finalize方法,垃圾回受器在垃圾回收时,并不会将该对象标为unreachable,而是先将其标为reachable,而后再将该对象移到一个称为finalization queue的队列中。在垃圾回收器回收垃圾的同时,.NET运行时环境开启另外一个专处理该对列中的垃圾清理工作。

GC类
客户端可以显式的调用GC类的静态方法Collection()进行垃圾回收工作。这将会伴随扫描对象图、线程上下文交换、线程挂起/唤醒、潜在磁盘访问、使用映射类读取元数据等一系列操作。所以我们通常不会手工调整垃圾回收工作。初始化垃圾回收器同时是因为对象实现了Finalize方法,因为垃圾回收器不一定执行,也就是说,Finalize也不一定被调用。为此,可以通过手工调用垃圾回收器以保证资源清理工作的正常完成。

Finalize()的实现
Finalize具有不确定性(不能确定具体什么时候执行,甚至也可能更本不执行),这可能延迟对象所包含资源的释放以及应用程序的可度量和执行性能。
在实现该方法时,应该注意的几点问题:
最好调用的基类Finalize:
base.Finalize(  );
任何对象都继承自Object,而该对象有一包含任何代码的Finalize方法。
必须使用protected;
在方法体内避免挂起调用,因为这将挂起Finalization Queue队列中所有的对象的Finalize;
Finalization不能作为线程方法。Finalize方法是运行在垃圾回收器线程上的,而不是其它的线程中。
进入Finalization Queue的顺序是不确定的,实现了Finalize的对象可以按任意的次序入队。
即便是在该对象资源释放出现错误,其父类资源还是应该释放的。也即应该添加如下代码:
protected virtual void Finalize(  ){
try
        {  /* Object cleanup here */ }
finally
        {  base.Finalize(  );}
}
在C#中,可以通过定义析构方法来实现Finalize。如C#代码:
public class MyClass {
   public MyClass(  ){}  
   ~MyClass(  )
     { //Your destructor code goes here.}
 }
在编译时,将会被替换为:
   public class MyClass{
       public MyClass(  ){}  
       protected virtual void Finalize(  ){
          try
           { //Your destructor code goes here. }
          finally
           { base.Finalize(  );}
       }
    }
在C#中不允许在一个类中同时定义析构方法和Finalize,我们只可以选择实现其一。

确定Finalization
在所有确定Finalization技术中,对象要求显式的告知什么时候不在被使用。为使确定Finalization技术工作,首先,必须允许客户端显式的指示资源释放的顺序。

Open/Close:
使用该技术释放资源,在下次需要使用时不需要重新创建对象,我们要做的只是调用对象的Open/Close方法。
文件使用就是使用该技术的一个很好的样例,当我们需要使用文件时打开文件,不需要就关闭文件。
Close方法使用在共享上比COM引用技术要复杂一点。

Dispose   
使用Dispose同使用Finalize相似,当客户端代码调用了Dispose,对象必须释放它所包含的所有资源,所有其它客户端也不再能访问这个对象。与Finalize不同点在于Dispose不需要等待垃圾回收器回收垃圾时才清理资源。
实现IDisposable接口使得我们更容易决定在那调度和如何实现Dispose方法。该接口定义于System命名空间:
public interface IDisposable
    { void Dispose(  );  }
例:
public class MyClass : IDisposable { 
  public void Dispose(  ) 
    {  //Do object cleanup and call base.Dispose(  ) if it has one } 
    //More methods and resources
}
在客户端中,我们就可以通过判断(查询)对象实例是否实现了IDisposable接口:
IMyInterface obj = new MyClass(  );
//Do something with obj.
    //And then Client wants to dispose of whatever needs disposing:
    IDisposable disposable = obj as IDisposable;
    if(disposable != null) { disposable.Dispose(  );}
如果不使用接口技术,我们在编写客户端代码时,就必须清楚的知道该类是否实现了Dispose方法,而在类修改时我们更难以知道是否还实现Dispose方法。
若类实现了Dispose()或IDisposable,在访问对象时,最好将其放在try/finally结构中。这样使用对象时即便抛出异常,应用程序也能够正常的工作。
用try/finally结构时,当卷入多个对象代码就比较杂乱,C#语言中提供了using关键字替代。C#中,using替代该结构的还有析构方法。两种情况也都是为清理资源而工作。
using(obj)  {  obj.SomeMethod(  ); }
C#编译器回将其转换为:
try
    {  obj.SomeMethod(  );  }
finally
{ if(obj != null)
       {  IDisposable disposable = obj;
          disposable.Dispose(  );
      }
    }
当然我们可以有如下代码:
using(obj1)
using(obj2)
using(obj3)
  {  obj1.SomeMethod(  );
     obj2.SomeMethod(  );   obj3.SomeMethod(  );
  }
使用using有个倾向,默认隐式的将对象转化为IDisposable接口。如果对象没有实现IDisposable接口,编译器不会编译。为使编译器正常工作,我们有3种解决方式:一,对象继承/或实现了IDisposable;二,在using内将对象显式转换为实现了IDisposable的对象;三,在using内使用as。

USING声明和泛类
当使用泛类对象时,我们并不知道客户端的具体对象是否支持IDisposable,所以我们不能够直接使用using声明:
public class MyClass<T>
    { public void SomeMethod(T t)
       {  using(t) {//Does not compile } }
    }
当使用泛类参数时,可以使用约束来限制泛类:
public class MyClass<T> where T : IDisposable

   注: 在原书中认为,我们不可以使用接口作为泛类的具体类型。如下代码并不能通过编译:
public interface IMyInterface {}
public class SomeClass : IMyInterface,IDisposable {...}
public class MyClass<T> where T : IDisposable
{
   public void SomeMethod(T t)
   {  using(t) {...} }
}
SomeClass someClass = new SomeClass(  );
MyClass<IMyInterface> obj = new MyClass<IMyInterface>(  ); //不编译
obj.SomeMethod(someClass);
其实这是作者的误解,泛类MyClass本身就要求其传入的参数实现IDisposable接口,而不仅仅是SomeMethod方法要求受到约束。而若IMyInterface实现了该接口,这段代码就能过通过实现。

Dispose和Finalize
Dispose和Finalize并不互斥,我们通常两者同时使用。使用Dispose并不能保证客户端真的就调用了它,在出现异常的时候我们往往就不能正常的调用Dispose,为此我们实现Finalize以便在此情况下释放资源。另外,如果客户端调用了Dispose,我们也就没有必要在继续调用Finalize。
垃圾回收器是通过检测现场是否有Finalize,若检测到就会将对象放入Finalization Queue。为此,如果调用了Dispose方法,应该压制Finalize的调用。我们通过调用GC类的静态方法SuppressFinalize来实现:
public static void SuppressFinalize(object obj);
其中obj就是我们这里欲释放资源的对象。
注:两种方法在应用程序中的功能应该是相同的,而且要保证释放基类对象的资源。对Dispose不同的线程都要有其调用句柄。

Deterministic Finalization 模板
public class BaseClass: IDisposable
    {
       private bool m_Disposed = false;
       protected bool Disposed

get { lock(this) { return m_Disposed;} }
       }
//Do not make Dispose(  ) virtual
// you should prevent subclasses from overriding
       public void Dispose(  )
       {
          lock(this)
{
             //Check to see if Dispose(  ) has already been called
             if(m_Disposed == false){
                Cleanup(  );
                m_Disposed = true;
                //Take yourself off the finalization queue
                //to prevent finalization from executing a second time.
                GC.SuppressFinalize(this);
             }
          }
       }
       protected virtual void Cleanup(  )
       {  /*Do cleanup here*/  }
       //Destructor will run only if Dispose(  ) is not called.
       //Do not provide destructors in types derived from this class.
       ~BaseClass(  ) 
       {  Cleanup(  ); }
       public void DoSomething(  )
       {
          if(Disposed) //verify in every method
          { throw new ObjectDisposedException("Object is already disposed"); }
       }
    }
     
public class SubClass1 : BaseClass
    {
       protected override void Cleanup(  )
       {
          try  {/*Do cleanup here*/  }
          finally
          {  base.Cleanup(  ); }  //Call base class 
       }
    }
在该模板中,派生类的资源清理工作完全的封装在cleanup方法中,我们可以通过override重新编写派生类的cleanup方法。然而我们实际调用的是Dispose和析构方法释放资源。使用模板我们可以通过调用对象的相应Dispose释放资源。例:
public class SubClass2 : BaseClass{ … }
调用SubClass1的Cleanup :
    SubClass1 a = new SubClass2(  );
a.Dispose(  );
通过转换调用SubClass2的Cleanup:
    SubClass1 b = new SubClass2(  );
    ((SubClass2)b).Dispose(  );
通过接口释放SubClass2对象包含的资源:
    IDisposable c = new SubClass2(  );
    c.Dispose(  );
通过转换释放SubClass1类对象包含的资源(即调用SubClass1的Cleanup):
    SubClass2 d = new SubClass2(  );
    ((SubClass1)d).Dispose(  );
直接释放SubClass1类对象包含的资源:
    SubClass1 e = new SubClass1(  );
    e.Dispose(  );
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值