C++ Programmer's Cookbook

{C++ 基础} {C++ 高级} {C#界面,C++核心算法} {设计模式} {C#基础}

共9页: 1 2 3 4 5 6 7 8 9 
实际上,有些时候两者是结合使用的。我见过BuilderFactory。在Java的DOM模型中就使用了BuilderFactory。

Java 环境中,解析文件是一个三步过程:

1、创建 DocumentBuilderFactory。 DocumentBuilderFactory 对象创建 DocumentBuilder。
2、创建 DocumentBuilder。 DocumentBuilder 执行实际的解析以创建 Document 对象。
3、解析文件以创建 Document 对象。

关键代码如下:

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import org.w3c.dom.Document;

public class OrderProcessor {
public static void main (String args[]) {
File docFile = new File("orders.xml");
Document doc = null;
try {

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.parse(docFile);

} catch (Exception e) {
System.out.print("Problem parsing the file: "+e.getMessage());
}
}
}

很有创意的!
提供一个创建一组相关或无关对象的接口,而没有指定具体的类.
七、 在什么情况下使用建造者模式
以下情况应当使用建造者模式:

1、 需要生成的产品对象有复杂的内部结构。
2、 需要生成的产品对象的属性相互依赖,建造者模式可以强迫生成顺序。
3、 在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到。

使用建造者模式主要有以下效果:

1、 建造模式的使用使得产品的内部表象可以独立的变化。使用建造者模式可以使客户端不必知道产品内部组成的细节。
2、 每一个Builder都相对独立,而与其它的Builder无关。
3、 模式所建造的最终产品更易于控制。
definition
Separate the construction of a complex object from its representation so that the same construction process can create different representations.

definition
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
definition
Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

上面代码开发过程中可以如此安排。1)由系统架构师设计好抽象产品和抽象工厂。2.1)多组并行开发具体产品和具体工厂。2.2)与此同时另外一组使用依赖注入技术开发主程序。在1的工作完成后2.1与2.2是可以并行的。按上面方案很可能产生3个Assembly。最后通过配置文件完成组装。


非常正确。 事实上,这个设计模式用在组件开发中是非常有用的,而且可以通过reflect并把具体的类名已经产生实例的参数传入工厂来产生实例。 java和csharp都是通用的,在这个点上。
想起这个题目,不得不由衷地佩服MS的细心度,我们想到了没有想到的他都给我们想到了。让我们尽可能的解放coding之苦。

  程序员,不得不在thinking 和coding之间进行痛苦的折磨,然而coding占去了我们的大量宝贵的时间。我们重复重复又重复着那些一辈子都不会忘掉的代码,重复重复又重复的重复着那些被我们敲烂的代码。

  发现这个还是一次偶然的机会,我自定义了一个Prop函数,结果在按下去的时候不小心碰到了Tab键结果出现了以下的代码:

private int myVar; public int MyProperty { get { return myVar;} set { myVar = value;} }

  熟悉的朋友应当已经发现这是一个标准的设置属性的字段。为什么我输入了Prop会自出来这些呢?带着疑问找到了google大法,原来已经早就有人发现了:这是2005的一个特色之一:代码段。

  代码段是提供了将现成的代码段插入到项目中的方法。它可以方便的把我们常用的一些代码用"快捷键"的方式让我们迅速的输入到IDE中。这里我不知道怎么形容,暂且用快捷键来代替吧。

  具体的输入方法:例如:我们输入Prop然后回车,接着按着Tab几秒钟,IDE就会自动给我们添加上去代码段所定义的代码。而且支持可修改部分,我们可以方便的接着使用Tab来穿梭于各个可修改部分。

  有人会有疑问,这些代码段怎么来的呢?IDE已经给我们自带了一部分,我们也可以根据自己的偏好来进行自行创建。

  打开VS2005的 \VC#\Snippets\2052\Visual C# 目录,我们会发现有一些以.snippet结尾的文件,这些就是系统给我们默认定义好的代码段。我们用文本文档打开,不难发现其实就是标准的XML文件。现对照if代码段进行一些讲解(对应文件为if.snippet)。看起代码:

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"
<CodeSnippet Format="1.0.0">//以上是代码段的一个标准格式 <Header>
<Title>if</Title>
//代码段的说明部分,指定代码段的标题。可以显示在代码段插入和代码段管理器的代码段说明部分。
<Shortcut>if</Shortcut>
//代码段的快捷方式。就是输入后按着Tab自动产生代码
<Description>if 语句的代码段</Description>
//指定代码段内容的说明信息,它可以给我们在使用代码段的时候提供工具提示,并且可以在代码段管理器中提供说明
<Author>Microsoft Corporation</Author>
//版权信息
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
<SnippetType>SurroundsWith</SnippetType>
</SnippetTypes>
//以上部分指定vs如何插入代码段。SurroundsWith允许代码段放置在选定代码快的周围,而Expansion的值允许将代码段插入光标所在的位置。
</Header>
<Snippet>
//此处开始指定代码的引用、导入、声明代码。主体部分
<Declarations>
//指定当前作为代码段组成部分的文本和对象,对代码段可以编辑部分。
<Literal>//<Literal> 段定义可编辑的代码段的文本。他用与标识对代码快的替换。我们可以使用Tab来进行切换修改。可以按照函数的概念来理解。这里就是我们传递参数值
<ID>expression</ID>
//替代标识
<ToolTip>要计算的表达式</ToolTip>//提示文本
<Default>true</Default>//默认值
</Literal>//若需多个,则可重复<Literal>代码段。
</Declarations>
<Code Language="csharp">
<![CDATA[if ($expression$) { $selected$ $end$ }]]>
</Code>//看code部分代码段。这里就是我们的核心部分。其中Language可以指定我们在用什么语言开发的时候使用此类代码段。
<! [CDATA[部分就是我们标准的C#语法了。if ($expression$)看清楚if()括号部分的代码,这里就是使用到我们在<Literal>部分定义的替代标识了。以$开始和结束,由于我们在定义的时候制定了默认值为true,所以在使用代码段的时候等价于if(true)。而括弧部分就是主体$selected$表示此处可以被选择,$ end$表示代码段结束。
</Snippet>
</CodeSnippet>
</CodeSnippets>

  看,通过上边的分析,代码段是不是很简单。我们在对照一下核心部分代码:

  代码段部分代码:

if ($expression$) { $selected$ $end$ }

  正常语句代码:

if (条件) { 代码 }

  了解了这些知识,那么我们该如何创建自己的代码段呢。最简单的方法就是用记事本,当然要十分熟悉这些语句。另外IDE也给我们提供了创建文件的方法。

  [文件]→[新建文件] →[XML文件],重新命名为:mytest. Snippet,一定要以snippet结尾,否则以后没有办法使用此代码段。参照上边的讲解,相信大家不难以葫芦画瓢来创建自己的代码段。

  建立好了,我们该怎么使用呢?[工具]→[代码段管理器] →[导入]即可。代码段管理器可以方便的浏览现有的代码段。我们也可以把自己做好的代码段共享给别人,只要别人通过代码段管理器就可以导入制订好的代码段。很方便。

  另外我们还可以创建VSI安装文件来进行安装使用。相信大部分人对VSI安装文件都有所了解了,熟悉的初学者开发工具包就是利用VSI文件发布的。具体的方法请看我的后续文章。用好2005之创建安装程序 (.vsi)

  通过以上介绍,相信大家会对代码段有一个很清楚的了解。我们可以通过"代码段管理器"方便的浏览已经存在的代码段以及它们的含义。

  代码段是预编写的代码片段,您可以随时使用 Visual Studio 2005 将这些代码片段插入到您的应用程序中。这些代码段减少了键入重复代码或搜索示例所用的时间,从而提高了工作效率。

  我们的口号是:只做对的,不做慢的。欢迎大家进行交流。

单例模式的特点:

单例类只能有一个实例。
单例类必须自己创建自己的唯一实例。
单例类必须给所有其它对象提供这一实例。
单例模式应用:

每台计算机可以有若干个打印机,但只能有一个Printer Spooler,避免两个打印作业同时输出到打印机。
一个具有自动编号主键的表可以有多个用户同时使用,但数据库中只能有一个地方分配下一个主键编号。否则会出现主键重复。
在什么情形下使用单例模式:
使用Singleton模式有一个必要条件:在一个系统要求一个类只有一个实例时才应当使用单例模式。反过来,如果一个类可以有几个实例共存,就不要使用单例模式。

注意:

不要使用单例模式存取全局变量。这违背了单例模式的用意,最好放到对应类的静态成员中。

不要将数据库连接做成单例,因为一个系统可能会与数据库有多个连接,并且在有连接池的情况下,应当尽可能及时释放连接。Singleton模式由于使用静态成员存储类实例,所以可能会造成资源无法及时释放,带来问题。
sealed class Singleton
{
private Singleton();
public static readonly Singleton Instance=new Singleton();
}
这使得代码减少了许多,同时也解决了线程问题带来的性能上损失。那么它又是怎样工作的呢?

注意到,Singleton类被声明为sealed,以此保证它自己不会被继承,其次没有了Instance的方法,将原来_instance成员变量变成public readonly,并在声明时被初始化。通过这些改变,我们确实得到了Singleton的模式,原因是在JIT的处理过程中,如果类中的static属性被任何方法使用时,.NET Framework将对这个属性进行初始化,于是在初始化Instance属性的同时Singleton类实例得以创建和装载。而私有的构造函数和readonly(只读)保证了Singleton不会被再次实例化,这正是Singleton设计模式的意图。
非托管

在.net 编程环境中,系统的资源分为托管资源和非托管资源。
对于托管的资源的回收工作,是不需要人工干预回收的,而且你也无法干预他们的回收,所能够做的只是了解.net CLR如何做这些操作。也就是说对于您的应用程序创建的大多数对象,可以依靠 .NET Framework 的垃圾回收器隐式地执行所有必要的内存管理任务。

对于非托管资源,您在应用程序中使用完这些非托管资源之后,必须显示的释放他们,例如System.IO.StreamReader的一个文件对象,必须显示的调用对象的Close()方法关闭它,否则会占用系统的内存和资源,而且可能会出现意想不到的错误。

我想说到这里,一定要清楚什么是托管资源,什么是非托管资源了?

最常见的一类非托管资源就是包装操作系统资源的对象,例如文件,窗口或网络连接,对于这类资源虽然垃圾回收器可以跟踪封装非托管资源的对象的生存期,但它不了解具体如何清理这些资源。还好.net Framework提供了Finalize()方法,它允许在垃圾回收器回收该类资源时,适当的清理非托管资源。如果在MSDN Library 中搜索Finalize将会发现很多类似的主题,这里列举几种常见的非托管资源:ApplicationContext,Brush,Component,ComponentDesigner,Container,Context,Cursor,FileStream,Font,Icon,Image,Matrix,Object,OdbcDataReader,OleDBDataReader
,Pen,Regex,Socket,StreamWriter,Timer,Tooltip 等等资源。可能在使用的时候很多都没有注意到!

关于托管资源,就不用说了撒,像简单的int,string,float,DateTime等等,.net中超过80%的资源都是托管资源。

非托管资源如何释放,.NET Framework 提供 Object.Finalize 方法,它允许对象在垃圾回收器回收该对象使用的内存时适当清理其非托管资源。默认情况下,Finalize 方法不执行任何操作。默认情况下,Finalize 方法不执行任何操作。如果您要让垃圾回收器在回收对象的内存之前对对象执行清理操作,您必须在类中重写 Finalize 方法。然而大家都可以发现在实际的编程中根本无法override方法Finalize(),在C#中,可以通过析构函数自动生成 Finalize 方法和对基类的 Finalize 方法的调用。

例如:

~MyClass()

{

// Perform some cleanup operations here.

}

该代码隐式翻译为下面的代码。

protected override void Finalize()

{

try

{

// Perform some cleanup operations here.

}

finally

{

base.Finalize();

}

}

但是,在编程中,并不建议进行override方法Finalize(),因为,实现 Finalize 方法或析构函数对性能可能会有负面影响。一个简单的理由如下:用 Finalize 方法回收对象使用的内存需要至少两次垃圾回收,当垃圾回收器回收时,它只回收没有终结器(Finalize方法)的不可访问的内存,这时他不能回收具有终结器(Finalize方法)的不可以访问的内存。它改为将这些对象的项从终止队列中移除并将他们放置在标记为“准备终止”的对象列表中,该列表中的项指向托管堆中准备被调用其终止代码的对象,下次垃圾回收器进行回收时,就回收并释放了这些内存。
re: c#的反射 梦在天涯 2006-04-19 12:16
关于反射

提纲:
1、什么是反射
2、命名空间与装配件的关系
3、运行期得到类型信息有什么用
4、如何使用反射获取类型
5、如何根据类型来动态创建对象
6、如何获取方法以及动态调用方法
7、动态创建委托


1、什么是反射
Reflection,中文翻译为反射。
这是.Net中获取运行时类型信息的方式,.Net的应用程序由几个部分:‘程序集(Assembly)’、‘模块(Module)’、‘类型(class)’组成,而反射提供一种编程的方式,让程序员可以在程序运行期获得这几个组成部分的相关信息,例如:

Assembly类可以获得正在运行的装配件信息,也可以动态的加载装配件,以及在装配件中查找类型信息,并创建该类型的实例。
Type类可以获得对象的类型信息,此信息包含对象的所有要素:方法、构造器、属性等等,通过Type类可以得到这些要素的信息,并且调用之。
MethodInfo包含方法的信息,通过这个类可以得到方法的名称、参数、返回值等,并且可以调用之。
诸如此类,还有FieldInfo、EventInfo等等,这些类都包含在System.Reflection命名空间下。

2、命名空间与装配件的关系
很多人对这个概念可能还是很不清晰,对于合格的.Net程序员,有必要对这点进行澄清。
命名空间类似与Java的包,但又不完全等同,因为Java的包必须按照目录结构来放置,命名空间则不需要。

装配件是.Net应用程序执行的最小单位,编译出来的.dll、.exe都是装配件。

装配件和命名空间的关系不是一一对应,也不互相包含,一个装配件里面可以有多个命名空间,一个命名空间也可以在多个装配件中存在,这样说可能有点模糊,举个例子:
装配件A:
namespace N1
{
public class AC1 {…}
public class AC2 {…}
}
namespace N2
{
public class AC3 {…}
public class AC4{…}
}
装配件B:
namespace N1
{
public class BC1 {…}
public class BC2 {…}
}
namespace N2
{
public class BC3 {…}
public class BC4{…}
}

这两个装配件中都有N1和N2两个命名空间,而且各声明了两个类,这样是完全可以的,然后我们在一个应用程序中引用装配件A,那么在这个应用程序中,我们能看到N1下面的类为AC1和AC2,N2下面的类为AC3和AC4。
接着我们去掉对A的引用,加上对B的引用,那么我们在这个应用程序下能看到的N1下面的类变成了BC1和BC2,N2下面也一样。
如果我们同时引用这两个装配件,那么N1下面我们就能看到四个类:AC1、AC2、BC1和BC2。

到这里,我们可以清楚一个概念了,命名空间只是说明一个类型是那个族的,比如有人是汉族、有人是回族;而装配件表明一个类型住在哪里,比如有人住在北京、有人住在上海;那么北京有汉族人,也有回族人,上海有汉族人,也有回族人,这是不矛盾的。

上面我们说了,装配件是一个类型居住的地方,那么在一个程序中要使用一个类,就必须告诉编译器这个类住在哪儿,编译器才能找到它,也就是说必须引用该装配件。
那么如果在编写程序的时候,也许不确定这个类在哪里,仅仅只是知道它的名称,就不能使用了吗?答案是可以,这就是反射了,就是在程序运行的时候提供该类型的地址,而去找到它。
有兴趣的话,接着往下看吧。

3、运行期得到类型信息有什么用
有人也许疑问,既然在开发时就能够写好代码,干嘛还放到运行期去做,不光繁琐,而且效率也受影响。
这就是个见仁见智的问题了,就跟早绑定和晚绑定一样,应用到不同的场合。有的人反对晚绑定,理由是损耗效率,但是很多人在享受虚函数带来的好处的时侯还没有意识到他已经用上了晚绑定。这个问题说开去,不是三言两语能讲清楚的,所以就点到为止了。
我的看法是,晚绑定能够带来很多设计上的便利,合适的使用能够大大提高程序的复用性和灵活性,但是任何东西都有两面性,使用的时侯,需要再三衡量。

接着说,运行期得到类型信息到底有什么用呢?
还是举个例子来说明,很多软件开发者喜欢在自己的软件中留下一些接口,其他人可以编写一些插件来扩充软件的功能,比如我有一个媒体播放器,我希望以后可以很方便的扩展识别的格式,那么我声明一个接口:
public interface IMediaFormat
{
string Extension {get;}
Decoder GetDecoder();
}
这个接口中包含一个Extension属性,这个属性返回支持的扩展名,另一个方法返回一个解码器的对象(这里我假设了一个Decoder的类,这个类提供把文件流解码的功能,扩展插件可以派生之),通过解码器对象我就可以解释文件流。
那么我规定所有的解码插件都必须派生一个解码器,并且实现这个接口,在GetDecoder方法中返回解码器对象,并且将其类型的名称配置到我的配置文件里面。
这样的话,我就不需要在开发播放器的时侯知道将来扩展的格式的类型,只需要从配置文件中获取现在所有解码器的类型名称,而动态的创建媒体格式的对象,将其转换为IMediaFormat接口来使用。

这就是一个反射的典型应用。

4、如何使用反射获取类型
首先我们来看如何获得类型信息。
获得类型信息有两种方法,一种是得到实例对象
这个时侯我仅仅是得到这个实例对象,得到的方式也许是一个object的引用,也许是一个接口的引用,但是我并不知道它的确切类型,我需要了解,那么就可以通过调用System.Object上声明的方法GetType来获取实例对象的类型对象,比如在某个方法内,我需要判断传递进来的参数是否实现了某个接口,如果实现了,则调用该接口的一个方法:

public void Process( object processObj )
{
Type t = processsObj.GetType();
if( t.GetInterface(“ITest”) !=null )

}

另外一种获取类型的方法是通过Type.GetType以及Assembly.GetType方法,如:
Type t = Type.GetType(“System.String”);
需要注意的是,前面我们讲到了命名空间和装配件的关系,要查找一个类,必须指定它所在的装配件,或者在已经获得的Assembly实例上面调用GetType。
本装配件中类型可以只写类型名称,另一个例外是mscorlib.dll,这个装配件中声明的类型也可以省略装配件名称(.Net装配件编译的时候,默认都引用了mscorlib.dll,除非在编译的时候明确指定不引用它),比如:
System.String是在mscorlib.dll中声明的,上面的Type t = Type.GetType(“System.String”)是正确的
System.Data.DataTable是在System.Data.dll中声明的,那么:
Type.GetType(“System.Data.DataTable”)就只能得到空引用。
必须:
Type t = Type.GetType("System.Data.DataTable,System.Data,Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
这样才可以,大家可以看下面这个帖子:
http://expert.csdn.net/Expert/topic/2210/2210762.xml?temp=.1919977
qqchen的回答很精彩


5、如何根据类型来动态创建对象
System.Activator提供了方法来根据类型动态创建对象,比如创建一个DataTable:

Type t = Type.GetType("System.Data.DataTable,System.Data,Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");

DataTable table = (DataTable)Activator.CreateInstance(t);

例二:根据有参数的构造器创建对象
namespace TestSpace {
public class TestClass
{
private string _value;
public TestClass(string value) {
_value=value;
}
}
}

Type t = Type.GetType(“TestSpace.TestClass”);
Object[] constructParms = new object[] {“hello”}; //构造器参数
TestClass obj = (TestClass)Activator.CreateInstance(t,constructParms);

把参数按照顺序放入一个Object数组中即可

6、如何获取方法以及动态调用方法
namespace TestSpace
{
public class TestClass {
private string _value;
public TestClass() {
}
public TestClass(string value) {
_value = value;
}

public string GetValue( string prefix ) {
if( _value==null )
return "NULL";
else
return prefix+" : "+_value;
}

public string Value {
set {
_value=value;
}
get {
if( _value==null )
return "NULL";
else
return _value;
}
}
}
}

上面是一个简单的类,包含一个有参数的构造器,一个GetValue的方法,一个Value属性,我们可以通过方法的名称来得到方法并且调用之,如:

//获取类型信息
Type t = Type.GetType("TestSpace.TestClass");
//构造器的参数
object[] constuctParms = new object[]{"timmy"};
//根据类型创建对象
object dObj = Activator.CreateInstance(t,constuctParms);
//获取方法的信息
MethodInfo method = t.GetMethod("GetValue");
//调用方法的一些标志位,这里的含义是Public并且是实例方法,这也是默认的值
BindingFlags flag = BindingFlags.Public | BindingFlags.Instance;
//GetValue方法的参数
object[] parameters = new object[]{"Hello"};
//调用方法,用一个object接收返回值
object returnValue = method.Invoke(dObj,flag,Type.DefaultBinder,parameters,null);

属性与方法的调用大同小异,大家也可以参考MSDN

7、动态创建委托
委托是C#中实现事件的基础,有时候不可避免的要动态的创建委托,实际上委托也是一种类型:System.Delegate,所有的委托都是从这个类派生的
System.Delegate提供了一些静态方法来动态创建一个委托,比如一个委托:

namespace TestSpace {
delegate string TestDelegate(string value);
public class TestClass {
public TestClass() {
}
public void GetValue(string value) {
return value;
}
}
}

使用示例:
TestClass obj = new TestClass();

//获取类型,实际上这里也可以直接用typeof来获取类型
Type t = Type.GetType(“TestSpace.TestClass”);
//创建代理,传入类型、创建代理的对象以及方法名称
TestDelegate method = (TestDelegate)Delegate.CreateDelegate(t,obj,”GetValue”);

String returnValue = method(“hello”);

上面第一个的exe必须放在第二个的bin\下


secondDomain.ExecuteAssembly("AssemblyA.exe");调用其main()方法.
re: .NET Framework Resource Management 梦在天涯 2006-04-14 16:22
Finalize 和Dispose(bool disposing)和 Dispose() 的相同点:

这三者都是为了释放非托管资源服务的.

Finalize 和 Dispose() 和Dispose(bool disposing)的不同点:

Finalize是CRL提供的一个机制, 它保证如果一个类实现了Finalize方法,那么当该类对象被垃圾回收时,垃圾回收器会调用Finalize方法.而该类的开发者就必须在Finalize方法中处理 非托管资源的释放. 但是什么时候会调用Finalize由垃圾回收器决定,该类对象的使用者(客户)无法控制.从而无法及时释放掉宝贵的非托管资源.由于非托管资源是比较宝贵了,所以这样会降低性能.
Dispose(bool disposing)不是CRL提供的一个机制, 而仅仅是一个设计模式(作为一个IDisposable接口的方法),它的目的是让供类对象的使用者(客户)在使用完类对象后,可以及时手动调用非托管资源的释放,无需等到该类对象被垃圾回收那个时间点.这样类的开发者就只需把原先写在Finalize的释放非托管资源的代码,移植到Dispose(bool disposing)中. 而在Finalize中只要简单的调用 "Dispose(false)"(为什么传递false后面解释)就可以了.
这个时候我们可能比较疑惑,为什么还需要一个Dispose()方法?难道只有一个Dispose(bool disposing)或者只有一个Dispose()不可以吗?
答案是:
只有一个Dispose()不可以. 为什么呢?因为如果只有一个Dispose()而没有Dispose(bool disposing)方法.那么在处理实现非托管资源释放的代码中无法判断该方法是客户调用的还是垃圾回收器通过Finalize调用的.无法实现 判断如果是客户手动调用,那么就不希望垃圾回收器再调用Finalize()(调用GC.SupperFinalize方法).另一个可能的原因(:我们知道如果是垃圾回收器通过Finalize调用的,那么在释放代码中我们可能还会引用其他一些托管对象,而此时这些托管对象可能已经被垃圾回收了, 这样会导致无法预知的执行结果(千万不要在Finalize中引用其他的托管对象).



所以确实需要一个bool disposing参数, 但是如果只有一个Dispose(bool disposing),那么对于客户来说,就有一个很滑稽要求,Dispose(false)已经被Finalize使用了,必须要求客户以Dispose(true)方式调用,但是谁又能保证客户不会以Dispose(false)方式调用呢?所以这里采用了一中设计模式:重载 把Dispose(bool disposing)实现为 protected, 而Dispose()实现为Public,那么这样就保证了客户只能调用Dispose()(内部调用Dispose(true)//说明是客户的直接调用),客户无法调用Dispose(bool disposing).


范例如下:

public class BaseResource: IDisposable
{
//析构函数自动生成 Finalize 方法和对基类的 Finalize 方法的调用.默认情况下,一个类是没有析构函数的,也就是说,对象被垃圾回收时不会被调用Finalize方法
~BaseResource()
{
// 为了保持代码的可读性性和可维护性,千万不要在这里写释放非托管资源的代码
// 必须以Dispose(false)方式调用,以false告诉Dispose(bool disposing)函数是从垃圾回收器在调用Finalize时调用的
Dispose(false);
}


// 无法被客户直接调用
// 如果 disposing 是 true, 那么这个方法是被客户直接调用的,那么托管的,和非托管的资源都可以释放
// 如果 disposing 是 false, 那么函数是从垃圾回收器在调用Finalize时调用的,此时不应当引用其他托管对象所以,只能释放非托管资源
protected virtual void Dispose(bool disposing)
{

// 那么这个方法是被客户直接调用的,那么托管的,和非托管的资源都可以释放
if(disposing)
{
// 释放 托管资源
OtherManagedObject.Dispose();
}


//释放非托管资源
DoUnManagedObjectDispose();


// 那么这个方法是被客户直接调用的,告诉垃圾回收器从Finalization队列中清除自己,从而阻止垃圾回收器调用Finalize方法.
if(disposing)
GC.SuppressFinalize(this);

}

//可以被客户直接调用
public void Dispose()
{
//必须以Dispose(true)方式调用,以true告诉Dispose(bool disposing)函数是被客户直接调用的
Dispose(true);
}
}

上面的范例达到的目的:

1/ 如果客户没有调用Dispose(),未能及时释放托管和非托管资源,那么在垃圾回收时,还有机会执行Finalize(),释放非托管资源,但是造成了非托管资源的未及时释放的空闲浪费

2/ 如果客户调用了Dispose(),就能及时释放了托管和非托管资源,那么该对象被垃圾回收时,不回执行Finalize(),提高了非托管资源的使用效率并提升了系统性能


可以参考SqlConnection对象的New, Open, Close(内部调用Dispose())的使用经历可以加深对他们的理解.谢谢!
re: object 序列化 梦在天涯 2006-04-14 16:17
NET的对象序列化真是好东西,以后要多多利用。

  在使用.NET的序列化时,碰到过一些问题,还好,有丰富的MSDN可查,没有什么过不去的槛。在这里,把使用.NET序列化的经验小结一下。
  1. 基本确认XmlSerializer使用UTF8对序列化的XML文档编码。
  2. XmlSerializer只序列化声明为public的字段,属性,或带返回值的方法。
  3. 如果要序列化属性,那么该属性必须是可读写的,即必须包含get和set,而不能是readonly或writeonly。
  4. XmlAttribute,XmlAnyAttribute不能与XmlElement,XmlText,XmlAnyElement,XmlArray,XmlArrayItem一起使用。
  5. XmlRoot只能用于一个类,XmlType可用于所有类。
  6. 不同的类的XmlType不能相同,除非使用NameSpaces区分。

相关链接:
  ● 在.NET中实现对象序列化
  ● 对象序列化:使用System.Xml.Serialization命名空间
  ● 对象序列化:使用XmlSerializer走完最后一步
re: object 序列化 梦在天涯 2006-04-14 12:09
class must have

public members or getandset of properities,

then it can be serialized.
re: C#中重用c/c++旧模块 梦在天涯 2006-04-03 08:39
@sunmast
^_^,不好意思啊 !可以留下你的连接吗?
re: c#目录浏览 梦在天涯 2006-03-10 08:47
c++ 的目录浏览 ,使用windows API ,c++的分类里file and directory里有!
re: c#目录浏览 梦在天涯 2006-03-10 08:45
那位知道怎么把控制台程序转到windowsform程序,只需要引用forms dll吗?
re: CString详细讲解 梦在天涯 2006-03-03 17:31
为什么不可以啊,数组是没有什么要求的把!
在mfc里已经有自己定义好的啊!
加一条线到model中

Line pline = new Line(new Point3d(10,10,0),new Point3d(100,100,0));
Database db= Application.DocumentManager.MdiActiveDocument.Database;
//DBTransMan tm=db.TransactionManager;
using(Transaction trans=db.TransactionManager.StartTransaction())
{
BlockTable bt=(BlockTable)db.TransactionManager.GetObject(db.BlockTableId,OpenMode. ForRead,false);
BlockTableRecord btr=
(BlockTableRecord)db.TransactionManager.GetObject(bt[BlockTableRecord.ModelSpace],
OpenMode.ForWrite,false);
btr.AppendEntity(pline);
db.TransactionManager.AddNewlyCreatedDBObject(pline,true);
trans.Commit();
}
re: arx & c++ 常用函数代码 梦在天涯 2006-02-17 09:03
Finding the Active Viewports in Model Space

// Set some viewport information.
AcDbViewportTable* pViewportTable;
if (db.getViewportTable(pViewportTable, AcDb::kForRead)
== Acad::eOk)
{
// Find the first viewport and open it for write.
AcDbViewportTableRecord *pRecord;
if (pViewportTable->getAt(
"*ACTIVE", pRecord,
AcDb::kForWrite) == Acad::eOk)
{
pRecord->setCenterPoint(AcGePoint2d(0.5, 0.5));
pRecord->setHeight(1.0);
pRecord->setWidth(1.0);
pRecord->close();
}
pViewportTable->close();
}
re: CString详细讲解 梦在天涯 2006-02-10 15:25
CString 的长度有没有限制啊?是不是不能太长啊
???
re: uml 基本概念 梦在天涯 2006-02-10 11:38
用例模型和用例图
类图和对象图
交互图:顺序图和合作图
行为图:状态图和活动图
实现图:构件图和配置图
re: c++ file and directory 梦在天涯 2006-02-09 16:49
-------------------------------------------------------------------
、获得当前应用程序路径
#include < windows.h >
#include < string.h >

HINSTANCE hInst;
char szBuf[256];
char *p;

//拿到全部路径
GetModuleFileName(hInst,szBuf,sizeof(szBuf));

//分离路径和文件名。
p = szBuf;
while(strchr(p,'\\')) {
p = strchr(p,'\\');
p++;
}
*p = '\0';
//路径在szBuf理了。
re: C++中DOM写XML 梦在天涯 2006-02-08 20:50
原程序对有些机器不能通过的原因:


应该把IXMLDOMDocument3Ptr中的3改为2!

还有虽然用了namespace MSXML2,但是还必须在每个用到的定义如IXMLDOMElementPtr 前加上MSXML2::,这样便可以通过!



有知道为什么的吗?


re: C++中DOM写XML 梦在天涯 2006-01-23 17:01
是啊,要看你装的MSXML的版本啊,有的要用3,有的不用啊!
好像是这样啊!
re: 简单好用的读写ini文件的类 梦在天涯 2006-01-18 10:11
我觉的也是的,但是我们的头让先用ini,不知道使用起来那个效率更好些啊!
re: 简单好用的读写ini文件的类 梦在天涯 2006-01-17 14:51
恩,高!学习中!

可不可以在每个section后加一行空行那?

请高手指点!
re: c++ file and directory 梦在天涯 2006-01-12 16:39
也可以用searchpath()来找,是吗?可惜就是没有找到例子!
那位有的话,贡献一下啊,谢谢!
微软的2005对目前c++标注好多的都不支持!

增加了安全机制!
re: C++中DOM写XML 梦在天涯 2005-12-29 16:09
恩,太感谢拉!牛!

为什么说有些节点不能是数字那?是所有的吗?好像有的行啊!^_^!


哦,想起来拉,节点的命名首字母不能是()。。。。。。。^_^!~

但是element里的text,我们不用管它是中文还是英文,也不用管是身编码吗?只要我们在xml头指定encoding=“”就可以了吗?为什么有的时候加了encoding =“gb2312”,显示仍然是乱麻那~???


谢谢!
re: C++中DOM写XML 梦在天涯 2005-12-29 13:44
为什么createelement(item)时,item不能是由数字转换为的字符串!而且是有时可以有时不可,都发现好多次拉,难道时bug,还是英文版的缘故啊?
re: 强烈不满google mail 梦在天涯 2005-12-26 08:51
我的也是不行啊,根本就打不开登陆页面啊!真郁闷啊
那啊!

妳是說stl中的算法的速度慢嗎?

但是對于一般的問題我覺的都可以滿足的吧!

^_^!~~·
re: C++之AOP 梦在天涯 2005-12-15 16:53
不明白啊,太高级拉!还的多多的学习啊
re: 我是如何"crack"掉winrar的 梦在天涯 2005-12-15 16:48
niu 牛啊!
能不能说的详细点啊,最好是有详细的步骤啊!
大家回很感谢你的啊!
恩!是的啊,。net2003中有3个参数的版本啊,也可以自己定义一个比较函数,
sort(XXX.begin(),XXX.end(), less_second);//less_second自定义函数
unique(XXX.begin(),XXX.end(),equal_double);//equal_double自定义函数

但是再.net2005中没有啊,编译不通过啊,好像有 4个参数的版本啊!不知道怎么用啊


屏幕得到的点的坐标,取4位小数,比较!我的方法×10000,然后long啊!大家有没有更好的方法啊 !



谢谢 啊!
re: OGRE粒子系统之在烈火中永生 梦在天涯 2005-12-14 18:44
orge是作游戏的语言?跟什么语言类似啊?可不可以简单描述一下啊,或与c++比较一下啊 !
re: vc6函数调用浅析 梦在天涯 2005-12-14 18:41
怎么反汇编啊?老大指点一下啊 !
re: 有用但不常见的c++函数 梦在天涯 2005-11-07 08:49
不需要的啊,你可以在你的windows上可以运行的啊 !
楼上的楼上说的拷贝构造函数是不是每个节点都的重新new,啊?
共9页: 1 2 3 4 5 6 7 8 9 

公告

EMail:itech001#126.com

导航

统计

  • 随笔 - 461
  • 文章 - 4
  • 评论 - 746
  • 引用 - 0

常用链接

随笔分类

随笔档案

收藏夹

Blogs

c#(csharp)

C++(cpp)

Enlish

Forums(bbs)

My self

Often go

Useful Webs

Xml/Uml/html

搜索

  •  

积分与排名

  • 积分 - 1796270
  • 排名 - 5

最新评论

阅读排行榜