.Net 缓存代理类 的初步考虑.net

时间:2023-07-21 07:48:56 其他范文 收藏本文 下载本文

.Net 缓存代理类 的初步考虑.net(合集9篇)由网友“烦的不如不活了”投稿提供,以下是小编精心整理的.Net 缓存代理类 的初步考虑.net,希望对大家有所帮助。

.Net 缓存代理类 的初步考虑.net

篇1:.Net 缓存代理类 的初步考虑.net

EntityData为实体数据类 我们这里将实体数据进行缓存,缓存方法是 //从Web.Config取得对缓存过期的设置 ICacheItemExpiration[] ice=ExpireTerm.GetExpirationPolicy; //获得缓冲对象 CacheManager cm=CacheManager.GetCacheManager(); 存储 cm.Add(EntityKe

EntityData为实体数据类

我们这里将实体数据进行缓存,缓存方法是

//从Web.Config取得对缓存过期的设置

ICacheItemExpiration[] ice=ExpireTerm.GetExpirationPolicy();

//获得缓冲对象

CacheManager cm=CacheManager.GetCacheManager();

存储

cm.Add(EntityKeyCreator.CreateKey(entity),entity,ice,CacheItemPriority.Normal,null);

此类可以做实体代理类

using Microsoft.ApplicationBlocks.Cache;

///

/// 缓冲实体

///

///

实体数据

public static void CacheEntity(EntityData entity)

{

if(entity.IsEmpty)

return;

ICacheItemExpiration[] ice=ExpireTerm.GetExpirationPolicy();

//获得缓冲对象

CacheManager cm=CacheManager.GetCacheManager();

//存储

cm.Add(ExpireTerm.CreateKey(entity),entity,ice,CacheItemPriority.Normal,null);

}

ExpireTerm.class

using Microsoft.ApplicationBlocks.Cache;

internal static ICacheItemExpiration[] GetExpirationPolicy()

{

if(Object.Equals(configSetting,null))

{

configSetting = (ICacheItemExpiration[])ConfigurationSettings.GetConfig(“TopCoolExpireTerm”);

}

return configSetting;

}

internal static string CreateKey(EntityData entity)

{

DataSet ds = new DataSet();

DataColumn[] keyColumns;

if(entity.Tables.Contains(entity.MainTableName))

keyColumns = entity.Tables[entity.MainTableName].PrimaryKey;

else

keyColumns = entity.CurrentTable.PrimaryKey;//

StringBuilder sb = new StringBuilder(entity.EntityTypeName);

//用:分隔实体类列名

for(int i=0;i

{

sb.Append(“:”);

sb.Append(entity[keyColumns[i].ColumnName].ToString());

}

return sb.ToString();

}

注意:EntityData从DataSet 继承过来.

原文转自:www.ltesting.net

篇2:项目考虑因素及解决方案(.net)

暂时想到这么多,大伙看看还有什么需要考虑的,

1、多语言支持

使用.net 2.0支持的多语言解决方案,针对显示信息对资源文件进行不同语言之间的翻译。

2、系统更新

可使用WebService、WCF等通信方式实现本地、跨网络、跨平台的系统更新。

3、代码保护

为了保护知识产权,一般的做法都会对发布的组件进行混淆或者代码加密。混淆可以增加代码阅读难度,但无法阻止代码被反射。加密可以阻止一般反射软件对组件代码的反射,连程序的结构都无法获取。

如果是小型项目,各组件间的耦合度较低,而且为了节省成本,可以考虑使用免费的混淆器,比如Dotfuscator、Xenocode PostBuild等,

如果项目比较大,而且各组件之间调用关系复杂,为了减少混淆的难度,可以使用加密软件,加密过程也很简单,比如MaxtoCode、DNGuard等。

4、组件、系统授权

通过授权,您作为组件或控件作者,可以验证开发人员是否被授予使用您的组件或控件的权限,来保护自己的知识产权。可以使用System.ComponentModel.LicenseProvider 实现,也可使用第三方的授权软件,设定组件的使用期限、使用次数等。当然,也可不用那么复杂,自己实现一个License机制,通过启动程序检验License的有效性来决定是否启动程序。

5、负载均衡

突然想到这个,先放着,以后更新。

来自:项目考虑因素及解决方案(.net)

篇3:导出Excel类.net

procedure TClassForm.ClassToExcel; var ExcelObj, Excel, WorkBook, Sheet: OleVari ant ; OldCursor:TCursor; Row,Col: Integer; begin if not (adsClasses.Active and adsMaster.Active and adsTeachers.Active and adsCadres.Active) then exit; OldCur

procedure TClassForm.ClassToExcel;

var

ExcelObj, Excel, WorkBook, Sheet: OleVariant;

OldCursor:TCursor;

Row,Col: Integer;

begin

if not (adsClasses.Active and adsMaster.Active and adsTeachers.Active and adsCadres.Active) then

exit;

OldCursor:=Screen.Cursor;

Screen.Cursor:=crHourGlass;

try

ExcelObj := CreateOleObject('Excel.Sheet');

Excel := ExcelObj.Application;

Excel.Visible := True;

WorkBook := Excel.Workbooks.Add ;

Sheet:= WorkBook.Sheets[1];

except

MessageBox(GetActiveWindow,'无法调用Mircorsoft Excel! '+chr(13)+chr(10)+

'请检查是否安装了Mircorsoft Excel,','提示',MB_OK+MB_ICONINFORMATION);

Screen.Cursor:=OldCursor;

Exit;

end;

try

//班级

Row := 1;

Col := 1;

StringToExcelSheet(DataModuleStudents.GetClassName,Row,Col+1,Sheet);

//班主任

Row := Row + 1;

StringToExcelSheet('班主任:',Row,Col,Sheet);

Col := Col + 1;

StringToExcelSheet(DataModuleStudents.adsMaster.FieldByName('班主任姓名').AsString,Row,Col,Sheet);

//老师

Row := Row + 1;

Col := 1;

StringToExcelSheet('老师:',Row,Col,Sheet);

Row := Row + 1;

DataSetToExcelSheetEx(DataModuleStudents.adsTeachers,Row,Col,Sheet);

//班干部

Row := Row + DataModuleStudents.adsTeachers.RecordCount + 1;

StringToExcelSheet('班干部:',Row,Col,Sheet);

Row := Row + 1;

DataSetToExcelSheetEx(DataModuleStudents.adsCadres,Row,Col,Sheet);

//备注

Row := Row + DataModuleStudents.adsCadres.RecordCount + 1;

StringToExcelSheet('备注:',Row,Col,Sheet);

Row := Row + 1;

MemoFieldtoExcelSheet(DataModuleStudents.adsClasses.FieldByName('备注'),Row,Col,Sheet);

finally

Screen.Cursor := OldCursor;

end;

end;

原文转自:www.ltesting.net

篇4:第五章 类 (rainbow 翻译).net

第五章 类(1) 前一章讨论了数据类型和它们的用法,现在我们转移到C#中至关重要的结构――类。没有了类,就连简单的C#程序 都不能编译。这一章假定你知道了一个类的基本组成部分:方法、属性、构造函数和析构函数。 C#在其中增加了索引和事 件。 在这一章中,

第五章 类(1)

前一章讨论了数据类型和它们的用法。现在我们转移到C#中至关重要的结构――类。没有了类,就连简单的C#程序

都不能编译。这一章假定你知道了一个类的基本组成部分:方法、属性、构造函数和析构函数。 C#在其中增加了索引和事

件。

在这一章中,你学到下列有关类的话题。

。 使用构造函数和析构函数

。给类写方法

。给一个类增加属性存取标志

。实现索引

。创建事件并通过代表元为事件关联客户

。应用类、成员和存取修饰符。

5.1 构造函数和析构函数

在你可以访问一个类的方法、属性或任何其它东西之前, 第一条执行的语句是包含有相应类的构造函数。甚至

你自己不写一个构造函数,也会有一个缺省的构造函数提供给你。

class TestClass

{

public TestClass: base() {} // 由编译器提供

}

一个构造函数总是和它的类名相同,但是,它没有声明返回类型。总之,构造函数总是public的,你可以用它们来

初始化变量。

public TestClass()

{

// 在这给变量

// 初始化代码等等。

}

如果类仅包含静态成员(能以类型调用,而不是以实例调用的成员),你可以创建一个private的构造函数。

private TestClass() {}

尽管存取修饰符在这一章的后面将要大篇幅地讨论,但是private意味着从类的外面不可能访问该构造函数。所

以,它不能被调用,且没有对象可以自该类定义被实例化。

并不仅限于无参数构造函数――你可以传递初始参数来初始化成员。

public TestClass(string strName, int nAge) { ... }

作为一个C/C++程序员,你可能习惯于给初始化写一个附加的方法,因为在构造函数中没有返回值。当然,尽管在

C#中也没有返回值,但你可以引发一个自制的异常,以从构造函数获得返回值。更多有关异常处理的知识在第七章 “异常

处理”中有讨论。

但是,当你保留引用给宝贵的资源,应该想到写一个方法来解决:一个可以被显式地调用来释放这些资源。问题是

当你可以在析构函数(以类名的前面加“~”的方式命名)中做同样的事情时,为何还要写一个附加的方法.

public ~TestClass()

{

// 清除

}

你应该写一个附加方法的原因是垃圾收集器,它在变量超出范围后并不会立即被调用,而仅当间歇期间或内存条件满

足时才被触发。当你锁住资源的时间长于你所计划的时间时,它就会发生。因此,提供一个显式的释放方式是一个好主

意,它同样能从析构函数中调用。

public void Release()

{

// 释放所有宝贵的资源

}

public ~TestClass()

调用析构函数中的释放方法并不是必要的――总之,垃圾收集会留意释放对象。但没有忘记清除是一种良好的习惯。

5.2 方法

既然对象能正确地初始化和结束,所剩下来的就是往类中增加功能。在大多数情况下,功能的主要部分在方法中能得

到实现。你早已见过静态方法的使用,但是,这些是类型(类)的部分,不是实例(对象)。

为了让你迅速入门,我把这些方法的烦琐问题安排为三节:

。方法参数

。改写方法

。方法屏蔽

5.2.1 方法参数

因方法要处理更改数值,你多多少少要传递值给方法,并从方法获得返回值。以下三个部分涉及到由传递值和为调用者

获取返回结果所引起的问题。

。输入参数

。引用参数

。输出参数

5.2.1.1 输入参数

你早已在例子中见过的一个参数就是输入参数。你用一个输入参数通过值传递一个变量给一个方法――方法的变量被调

用者传递进来的值的一个拷贝初始化。清单5.1 示范输入参数的使用。

清单 5.1 通过值传递参数

1: using System;

2:

3: public class SquareSample

4: {

5: public int CalcSquare(int nSideLength)

6: {

7: return nSideLength*nSideLength;

8: }

9: }

10:

11: class SquareApp

12: {

13: public static void Main()

14: {

15: SquareSample sq = new SquareSample();

16: Console.WriteLine(sq.CalcSquare(25).ToString());

17: }

18: }

因为我传递值而不是引用给一个变量,所以当调用方法时(见第16行),可以使用一个常量表达式(25)。整型结果被传回

给调用者作为返回值,它没有存到中间变量就被立即显示到屏幕上 。

输入参数按C/C++程序员早已习惯的工作方式工作。如果你来自VB,请注意没有能被编译器处理的隐式ByVal或ByRef―

―如果没有设定,参数总是用值传递。

这点似乎与我前面所陈述的有冲突:对于一些变量类型,用值传递实际上意味着用引用传递。迷惑吗? 一点背景知识

也不需要:COM中的东西就是接口,每一个类可以拥有一个或多个接口。一个接口只不过是一组函数指针,它不包含数据。

重复该数组会浪费很多内存资源;所以,仅开始地址被拷贝给方法,它作为调用者,仍然指向接口的相同指针。那就是为

什么对象用值传递一个引用。

5.2.1.2 引用参数

尽管可以利用输入参数和返回值建立很多方法,但你一想到要传递值并原地修改它(也就是在相同的内存位置),就没

有那么好运了。这里用引用参数就很方便。

void myMethod(ref int nInOut)

因为你传递了一个变量给该方法(不仅仅是它的值),变量必须被初始化。否则,编译器会报警。清单 5.2 显示如何用

一个引用参数建立一个方法。

清单 5.2 通过引用传递参数

1: // class SquareSample

2: using System;

3:

4: public class SquareSample

5: {

6: public void CalcSquare(ref int nOne4All)

7: {

8: nOne4All *= nOne4All;

9: }

10: }

11:

12: class SquareApp

13: {

14: public static void Main()

15: {

16: SquareSample sq = new SquareSample();

17:

18: int nSquaredRef = 20; // 一定要初始化

19: sq.CalcSquare(ref nSquaredRef);

20: Console.WriteLine(nSquaredRef.ToString());

21: }

22: }

正如所看到的,所有你要做的就是给定义和调用都加上ref限定符。因为变量通过引用传递,你可以用它来计算出结果

并传回该结果。但是,在现实的应用程序中,我强烈建议要用两个变量,一个输入参数和一个引用参数。

5.2.1.3 输出参数

传递参数的第三种选择就是把它设作一个输出参数。正如该名字所暗示,一个输出参数仅用于从方法传递回一个结果。

它和引用参数的另一个区别在于:调用者不必先初始化变量才调用方法。这显示在清单5.3中。

清单 5.3 定义一个输出参数

1: using System;

2:

3: public class SquareSample

4: {

5: public void CalcSquare(int nSideLength, out int nSquared)

6: {

7: nSquared = nSideLength * nSideLength;

8: }

9: }

10:

11: class SquareApp

12: {

13: public static void Main()

14: {

15: SquareSample sq = new SquareSample();

16:

17: int nSquared; // 不必初始化

18: sq.CalcSquare(15, out nSquared);

19: Console.WriteLine(nSquared.ToString());

20: }

21: }

5.2.2 改写方法

面向对象设计的重要原则就是多态性。不要理会高深的理论,多态性意味着:当基类程序员已设计好用于改写的方法

时,在派生类中,你就可以重定义(改写)基类的方法。基类程序员可以用 virtual 关键字设计方法:

virtual void CanBOverridden()

当从基类派生时,所有你要做的就是在新方法中加入override关键字:

override void CanBOverridden()

当改写一个基类的方法时,你必须明白,不能改变方法的访问属性――在这章的后面,你会学到更多关于访问修饰符

的知识,

除了改写基类方法的事实外,还有另一个甚至更重要的改写特性。当把派生类强制转换成基类类型并接着调用虚拟方

法时,被调用的是派生类的方法而不是基类的方法。

((BaseClass)DerivedClassInstance).CanBOverridden();

为了演示虚拟方法的概念,清单 5.4 显示如何创建一个三角形基类,它拥有一个可以被改写的成员方法

(ComputeArea)。

清单 5.4 改写一个基类的方法

1: using System;

2:

3: class Triangle

4: {

5: public virtual double ComputeArea(int a, int b, int c)

6: {

7: // Heronian formula

8: double s = (a + b + c) / 2.0;

9: double dArea = Math.Sqrt(s*(s-a)*(s-b)*(s-c));

10: return dArea;

11: }

12: }

13:

14: class RightAngledTriangle:Triangle

15: {

16: public override double ComputeArea(int a, int b, int c)

17: {

18: double dArea = a*b/2.0;

19: return dArea;

20: }

21: }

22:

23: class TriangleTestApp

24: {

25: public static void Main()

26: {

27: Triangle tri = new Triangle();

28: Console.WriteLine(tri.ComputeArea(2, 5, 6));

29:

30: RightAngledTriangle rat = new RightAngledTriangle();

31: Console.WriteLine(rat.ComputeArea(3, 4, 5));

32: }

33: }

基类Triangle定义了方法ComputeArea。它采用三个参数,返回一个double结果,且具有公共访问性。从Triangle类派

生出的是RightAngledTriangle,它改写了ComputeArea 方法,并实现了自己的面积计算公式。两个类都被实例化,且在命

名为TriangleTestApp的应用类的Main() 方法中得到验证。

我漏了解释第14行:

class RightAngledTriangle : Triangle

在类语句中冒号(:)表示RightAngledTriangle从类 Triangle派生。那就是你所必须要做的,以让C#知道你想把

Triangle当作RightAngledTriangle的基类。

当仔细观察直角三角形的ComputeArea方法时,你会发现第3个参数并没有用于计算。但是,利用该参数就可以验证是

否是“直角”。如清单5.5所示。

清单 5.5 调用基类实现

1: class RightAngledTriangle:Triangle

2: {

3: public override double ComputeArea(int a, int b, int c)

4: {

5: const double dEpsilon = 0.0001;

6: double dArea = 0;

7: if (Math.Abs((a*a + b*b - c*c)) >dEpsilon)

8: {

9: dArea = base.ComputeArea(a,b,c);

10: }

11: else

12: {

13: dArea = a*b/2.0;

14: }

15:

16: return dArea;

17: }

18: }

该检测简单地利用了毕达哥拉斯公式,对于直角三角形,检测结果必须为0。如果结果不为0,类就调用它基类的

ComputeArea来实现。

dArea = base.ComputeArea(a,b,c);

例子的要点为:通过显式地利用基类的资格检查,你就能轻而易举地调用基类实现改写方法。

当你需要实现其在基类中的功能,而不愿意在改写方法中重复它时,这就非常有帮助。

5.2.3 方法屏蔽

重定义方法的一个不同手段就是要屏蔽基类的方法。当从别人提供的类派生类时,这个功能特别有价值。看清单

5.6,假设BaseClass由其他人所写,而你从它派生出 DerivedClass 。

清单 5.6 Derived Class 实现一个没有包含于 Base Class中的方法

1: using System;

2:

3: class BaseClass

4: {

5: }

6:

7: class DerivedClass:BaseClass

8: {

9: public void TestMethod()

10: {

11: Console.WriteLine(“DerivedClass::TestMethod”);

12: }

13: }

14:

15: class TestApp

16: {

17: public static void Main()

18: {

19: DerivedClass test = new DerivedClass();

20: test.TestMethod();

21: }

22: }

在这个例子中, DerivedClass 通过TestMethod()实现了一个额外的功能。但是,如果基类的开发者认为把

TestMethod()放在基类中是个好主意,并使用相同的名字实现它时,会出现什么问题呢?(见清单5.7)

清单 5.7 Base Class 实现和 Derived Class相同的方法

1: class BaseClass

2: {

3: public void TestMethod()

4: {

5: Console.WriteLine(“BaseClass::TestMethod”);

6: }

7: }

8:

9: class DerivedClass:BaseClass

10: {

11: public void TestMethod()

12: {

13: Console.WriteLine(“DerivedClass::TestMethod”);

14: }

15: }

在优秀的编程语言中,你现在会遇到一个真正的 烦。但是,C#会给你提出警告:

hiding2.cs(13,14): warning CS0114: ‘DerivedClass.TestMethod()‘ hides inherited member ‘BaseClass.TestMethod

()‘. To make the current method override that implementation, add the override keyword. Otherwise add the

new keyword.

(hiding2.cs(13,14):警告 CS0114:‘DerivedClass.TestMethod()‘ 屏蔽了所继承的成员‘BaseClass.TestMethod()‘。要

想使当前方法改写原来的实现,加上 override关键字。否则加上新的关键字。)

具有了修饰符new,你就可以告诉编译器,不必重写派生类或改变使用到派生类的代码,你的方法就能屏蔽新加入的基类方

法。清单5.8 显示如何在例子中运用new修饰符。

清单 5.8 屏蔽基类方法

1: class BaseClass

2: {

3: public void TestMethod()

4: {

5: Console.WriteLine(“BaseClass::TestMethod”);

6: }

7: }

8:

9: class DerivedClass:BaseClass

10: {

11: new public void TestMethod()

12: {

13: Console.WriteLine(“DerivedClass::TestMethod”);

14: }

15: }

使用了附加的new修饰符,编译器就知道你重定义了基类的方法,它应该屏蔽基类方法。但是,如果你按以下方式编写:

DerivedClass test = new DerivedClass();

((BaseClass)test).TestMethod();

基类方法的实现就被调用了。这种行为不同于改写方法,后者保证大部分派生方法获得调用。

原文转自:www.ltesting.net

篇5:第五章 类 (rainbow 翻译).net

第五章 类 (2) 5.3 类属性 有两种途径揭示类的命名属性――通过域成员或者通过属性,前者是作为具有公共访问性的成员变量而被实现的;后 者并不直接回应存储位置,只是通过 存取标志(accessors)被访问。 当你想读出或写入属性的值时,存取标志限定了被实现的

第五章 类 (2)

5.3 类属性

有两种途径揭示类的命名属性――通过域成员或者通过属性。前者是作为具有公共访问性的成员变量而被实现的;后

者并不直接回应存储位置,只是通过 存取标志(aclearcase/“ target=”_blank“ >ccessors)被访问。

当你想读出或写入属性的值时,存取标志限定了被实现的语句。用于读出属性的值的存取标志记为关键字get,而要修

改属性的值的读写符标志记为set。

在你对该理论一知半解以前,请看一下清单5.9中的例子,属性SquareFeet被标上了get和set的存取标志。

清单 5.9 实现属性存取标志

1: using System;

2:

3: public class House

4: {

5: private int m_nSqFeet;

6:

7: public int SquareFeet

8: {

9: get { return m_nSqFeet; }

10: set { m_nSqFeet = value; }

11: }

12: }

13:

14: class TestApp

15: {

16: public static void Main()

17: {

18: House myHouse = new House();

19: myHouse.SquareFeet = 250;

20: Console.WriteLine(myHouse.SquareFeet);

21: }

22: }

House类有一个命名为SquareFeet的属性,它可以被读和写。实际的值存储在一个可以从类内部访问的变量中――如果

你想当作一个域成员重写它,你所要做的就是忽略存取标志而把变量重新定义为:

public int SquareFeet;

对于一个如此简单的变量,这样不错。但是,如果你想要隐藏类内部存储结构的细节时,就应该采用存取标志。在这种情

况下,set 存取标志给值参数中的属性传递新值。(可以改名,见第10行。)

除了能够隐藏实现细节外,你还可自由地限定各种操作:

get和set:允许对属性进行读写访问。

get only:只允许读属性的值。

set only:只允许写属性的值。

除此之外,你可以获得实现在set标志中有效代码的机会。例如,由于种种原因(或根本没有原因),你就能够拒绝一个新

值。最好是没有人告诉你它是一个动态属性――当你第一次请求它后,它会保存下来,故要尽可能地推迟资源分配。

5.4 索引

你想过象访问数组那样使用索引访问类吗 ?使用C#的索引功能,对它的期待便可了结。

语法基本上象这样:

属性 修饰符 声明 { 声明内容}

具体的例子为

public string this[int nIndex]

{

get { ... }

set { ... }

}

索引返回或按给出的index设置字符串。它没有属性,但使用了public修饰符。声明部分由类型string和this 组成用于表

示类的索引。

get和set的执行规则和属性的规则相同。(你不能取消其中一个。) 只存在一个差别,那就是:你几乎可以任意定义大括

弧中的参数。限制为,必须至少规定一个参数,允许ref 和out 修饰符。

this关键字确保一个解释。索引没有用户定义的名字,this 表示默认接口的索引。如果类实现了多个接口,你可以增加更

多个由InterfaceName.this说明的索引。

为了演示一个索引的使用,我创建了一个小型的类,它能够解析一个主机名为IP地址――或一个IP地址列表(以

www.microsoft.com为例 )。这个列表通过索引可以访问,你可以看一下清单5.10 的具体实现。

清单 5.10 通过一个索引获取一个IP地址

1: using System;

2: using System.Net;

3:

4: class ResolveDNS

5: {

6: IPAddress[] m_arrIPs;

7:

8: public void Resolve(string strHost)

9: {

10: IPHostEntry iphe = DNS.GetHostByName(strHost);

11: m_arrIPs = iphe.AddressList;

12: }

13:

14: public IPAddress this[int nIndex]

15: {

16: get

17: {

18: return m_arrIPs[nIndex];

19: }

20: }

21:

22: public int Count

23: {

24: get { return m_arrIPs.Length; }

25: }

26: }

27:

28: class DNSResolverApp

29: {

30: public static void Main()

31: {

32: ResolveDNS myDNSResolver = new ResolveDNS();

33: myDNSResolver.Resolve(”www.microsoft.com“);

34:

35: int nCount = myDNSResolver.Count;

36: Console.WriteLine(”Found IP‘s for hostname“, nCount);

37: for (int i=0; i < nCount; i++)

38: Console.WriteLine(myDNSResolver[i]);

39: }

40: }

为了解析主机名,我用到了DNS类,它是System .Net 名字空间的一部分。但是,由于这个名字空间并不包含在核心

库中,所以必须在编译命令行中引用该库:

csc /r:System.Net.dll /out:resolver.exe dnsresolve.cs

解析代码是向前解析的。在该 Resolve方法中,代码调用DNS类的静态方法GetHostByName,它返回一个IPHostEntry

对象。结果,该对象包含有我要找的数组――AddressList数组。在退出Resolve 方法之前,在局部的对象实例成员

m_arrIPs中,存储了一个AddressList array的拷贝(类型IPAddress 的对象存储在其中)。

具有现在生成的数组 ,通过使用在类ResolveDNS中求得的索引,应用程序代码就可以在第37至38行列举出IP地址。

(在第6章 ”控制语句“,有更多有关语句的信息。) 因为没有办法更改IP地址,所以仅给索引使用了get存取标志。为了

简单其见,我忽略了数组的边界溢出检查。

5.4 事件

当你写一个类时,有时有必要让类的客户知道一些已经发生的事件。如果你是一个具有多年编程经验的程序员,似乎有

很多的解决办法,包括用于回调的函数指针和用于ActiveX控件的事件接收(event sinks)。现在你将要学到另外一种把客

户代码关联到类通知的办法――使用事件。

事件既可以被声明为类域成员(成员变量),也可以被声明为属性。两者的共性为,事件的类型必定是代表元,而函

数指针原形和C#的代表元具有相同的含义。

每一个事件都可以被0或更多的客户占用,且客户可以随时关联或取消事件。你可以以静态或者以实例方法定义代表

元,而后者很受C++程序员的欢迎。

既然我已经提到了事件的所有功能及相应的代表元,请看清单5.11中的例子。它生动地体现了该理论。

清单5.11 在类中实现事件处理

1: using System;

2:

3: // 向前声明

4: public delegate void EventHandler(string strText);

5:

6: class EventSource

7: {

8: public event EventHandler TextOut;

9:

10: public void TriggerEvent()

11: {

12: if (null != TextOut) TextOut(”Event triggered“);

13: }

14: }

15:

16: class TestApp

17: {

18: public static void Main()

19: {

20: EventSource evsrc = new EventSource();

21:

22: evsrc.TextOut += new EventHandler(CatchEvent);

23: evsrc.TriggerEvent();

24:

25: evsrc.TextOut -= new EventHandler(CatchEvent);

26: evsrc.TriggerEvent();

27:

28: TestApp theApp = new TestApp();

29: evsrc.TextOut += new EventHandler(theApp.InstanceCatch);

30: evsrc.TriggerEvent();

31: }

32:

33: public static void CatchEvent(string strText)

34: {

35: Console.WriteLine(strText);

36: }

37:

38: public void InstanceCatch(string strText)

39: {

40: Console.WriteLine(”Instance “ + strText);

41: }

42: }

第4行声明了代表元(事件方法原形),它用来给第8行中的EventSource类声明TextOut事件域成员,

你可以观察到代

表元作为一种新的类型声明,当声明事件时可以使用代表元。

该类仅有一个方法,它允许我们触发事件。请注意,你必须进行事件域成员不为null的检测,因为可能会出现没有客

户对事件感兴趣这种情况。

TestApp类包含了Main 方法,也包含了另外两个方法,它们都具备事件所必需的信号。其中一个方法是静态的,而另

一个是实例方法。

EventSource 被实例化,而静态方法CatchEvent被预关联上了 TextOut事件:

evsrc.TextOut += new EventHandler(CatchEvent);

从现在起,当事件被触发时,该方法被调用。如果你对事件不再感兴趣,简单地取消关联:

evsrc.TextOut -= new EventHandler(CatchEvent);

注意,你不能随意取消关联的处理函数――在类代码中仅创建了这些处理函数。为了证明事件处理函数也和实例方法

一起工作,余下的代码建立了TestApp 的实例,并钩住事件处理方法。

事件在哪方面对你特别有用?你将经常在ASP+中或使用到WFC (Windows Foundation Classes)时,涉及到事件和代表

元。

5.5 应用修饰符

在这一章的学习过程中,你已经见过了象public、virtual等修饰符。欲以一种易于理解的方法概括它们,我把它们划

分为三节:

。类修饰符

。成员修饰符

。存取修饰符

5.5.1 类修饰符

到目前为止,我还没有涉及到类修饰符,而只涉及到了应用于类的存取修饰符。但是,有两个修饰符你可以用于类:

abstract――关于抽象类的重要一点就是它不能被实例化。只有不是抽象的派生类才能被实例化。派生类必须实现抽

象基类的所有抽象成员。你不能给抽象类使用sealed 修饰符。

sealed――密封 类不能被继承。使用该修饰符防止意外的继承,在.NET框架中的类用到这个修饰符。

要见到两个修饰符的运用,看看清单5.12 ,它创建了一个基于一个抽象类的密封类(肯定是一个十分极端的例子)。

清单 5.12 抽象类和密封类

1: using System;

2:

3: abstract class AbstractClass

4: {

5: abstract public void MyMethod();

6: }

7:

8: sealed class DerivedClass:AbstractClass

9: {

10: public override void MyMethod()

11: {

12: Console.WriteLine(”sealed class“);

13: }

14: }

15:

16: public class TestApp

17: {

18: public static void Main()

19: {

20: DerivedClass dc = new DerivedClass();

21: dc.MyMethod();

22: }

23: }

5.5.2 成员修饰符

与有用的成员修饰符的数量相比,类修饰符的数量很少。我已经提到了一些,这本书即将出现的例子描述了其它的成

员修饰符。

以下是有用的成员修饰符:

abstract――说明一个方法或存取标志不能含有一个实现。它们都是隐式虚拟,且在继承类中,你必须提供

override关键字。

const――这个修饰符应用于域成员或局部变量。在编译时常量表达式被求值,所以,它不能包含变量的引用。

event ――定义一个域成员或属性作为类型事件。用于捆绑客户代码到类的事件。

extern――告诉编译器方法实际上由外部实现。第10章 “和非受管代码互相操作” 将全面地涉及到外部代码。

override――用于改写任何基类中被定义为virtual的方法和存取标志。要改写的名字和基类的方法必须一致。

readonly――一个使用 readonly修饰符的域成员只能在它的声明或者在包含它的类的构造函数中被更改。

static――被声明为static的成员属于类,而不属于类的实例。你可以用static 于域成员、方法、属性、操作符甚至

构造函数。

virtual――说明方法或存取标志可以被继承类改写。

5.5.3 存取修饰符

存取修饰符定义了某些代码对类成员(如方法和属性)的存取等级。你必须给每个成员加上所希望的存取修饰符,否

则,默认的存取类型是隐含的。

你可以应用4个 存取修饰符之一:

public――任何地方都可以访问该成员,这是具有最少限制的存取修饰符。

protected――在类及所有的派生类中可以访问该成员,不允许外部访问。

private――仅仅在同一个类的内部才能访问该成员。甚至派生类都不能访问它。

internal――允许相同组件(应用程序或库)的所有代码访问。在.NET组件级别,你可以把它视为public,而在外部

则为private。

为了演示存取修饰符的用法,我稍微修改了Triangle例子,使它包含了新增的域成员和一个新的派生类(见清单

5.13)。

清单 5.13 在类中使用存取修饰符

1: using System;

2:

3: internal class Triangle

4: {

5: protected int m_a, m_b, m_c;

6: public Triangle(int a, int b, int c)

7: {

8: m_a = a;

9: m_b = b;

10: m_c = c;

11: }

12:

13: public virtual double Area()

14: {

15: // Heronian formula

16: double s = (m_a + m_b + m_c) / 2.0;

17: double dArea = Math.Sqrt(s*(s-m_a)*(s-m_b)*(s-m_c));

18: return dArea;

19: }

20: }

21:

22: internal class Prism:Triangle

23: {

24: private int m_h;

25: public Prism(int a, int b, int c, int h):base(a,b,c)

26: {

27: m_h = h;

28: }

29:

30: public override double Area()

31: {

32: double dArea = base.Area() * 2.0;

33: dArea += m_a*m_h + m_b*m_h + m_c*m_h;

34: return dArea;

35: }

36: }

37:

38: class PrismApp

39: {

40: public static void Main()

41: {

42: Prism prism = new Prism(2,5,6,1);

43: Console.WriteLine(prism.Area());

44: }

45: }

Triangle 类和 Prism 类现在被标为 internal。这意味着它们只能在当前组件中被访问。请记住“.NET组件”这

个术语指的是包装( packaging,),而不是你可能在COM+中用到的组件。Triangle 类有三个 protected成员,它们在构

造函数中被初始化,并用于面积计算的方法中。由于这些成员是protected 成员,所以我可以在派生类Prism中访问它们,

在那里执行不同的面积计算。Prism自己新增了一个成员m_h,它是私有的――甚至派生类也不能访问它。

花些时间为每个类成员甚至每个类计划一种保护层次,通常是个好主意。当需要引入修改时,全面的计划最终会帮

助你,因为没有程序员会愿意使用“没有文档”的类功能。

5.6 小结

这章显示了类的各种要素,它是运行实例(对象)的模板。在一个对象的生命期,首先被执行的代码是个构造函数。

构造函数用来初始化变量,这些变量后来在方法中用于计算结果。

方法允许你传递值、引用给变量,或者只传送一个输出值。方法可以被改写以实现新的功能,或者你可以屏蔽基类成

员,如果它实现了一个具有和派生类成员相同名字的方法。

命名属性可以被当作域成员(成员变量)或属性存取标志实现。后者是get和set存取标志,忽略一个或另外一个,你

可以创建仅写或仅读属性。存取标志非常适合于确认赋给属性的值。

C#类的另外一个功能是索引,它使象数组语法一样访问类中值成为可能。还有,如果当类中的某些事情发生时,你想

客户得到通知,要让它们与事件关联。

当垃圾收集器调用析构函数时,对象的生命就结束了。由于你不能准确地预测这种情况什么时候会发生,所以应该创

建一个方法以释放这些宝贵的资源,当你停止使用它们时。

原文转自:www.ltesting.net

篇6:自制性能测试类.net

商业软件包往往价格昂贵,并且需要一个过程之后才能有效地使用它们,针对这一点,本文拟实现一个简单有效的类,它能自动计算并报告函数、循环和代码块执行的时间。 自动化与简易性设计 利用类对象构造函数和析构函数的执行特性(它们分别在声明和销毁时执行)

商业软件包往往价格昂贵,并且需要一个过程之后才能有效地使用它们。针对这一点,本文拟实现一个简单有效的类,它能自动计算并报告函数、循环和代码块执行的时间。

自动化与简易性设计

利用类对象构造函数和析构函数的执行特性(它们分别在声明和销毁时执行),性能测试类的计时是在构造函数开始的,计算与报告某个操作的执行时间是在析构函数中进行的。测试仪提供毫秒级的结果。实现过程中将使用clock返回程序开始后的处理器时钟时间(与平台相关的时间单位)。宏CLK_TCK表示特定机器每秒时钟数。

性能测试类定义如下:

#include

class stopwatch

{

public:

stopwatch() : start(clock()){} //开始计时

~stopwatch();

private:

clock_t start;

};

构造函数将成员start初始化为当前的时钟。除了析构函数外没有定义其它的成员函数。析构函数再次调用clock(),计算构造对象后经过的时间并显示结果:

#include

using namespace std;

stopwatch::~stopwatch()

{

clock_t total = clock()-start; //获得所用时间

cout<<”此操作所用时间: “<<

cout< <”转换成秒数: “<< double(total/CLK_TCK) <

}

注意clock_t和CLK_TCK是整数。因此在进行除法操作前必须将它们转换成double类型。为了延时屏幕输出,在析构函数中可以加上下列代码:

char dummy;

cin >>dummy; //延时屏幕输出

另外也可以将不同性能侧面的结果写入性能日志文件。

用所创建的类测试性能

为了对代码块进行测试,先在代码块的开始创建一个本地类实例,假设要测试的代码是下列循环:

string *pstr[5000]; //指针数组

for (int i=0;i<5000;i++)

{

pstr[i] = new string;

}

此循环在堆中分配5000个串对象,

用大括弧将上面的代码块括起来并在代码块开始声明类对象实例:

{

stopwatch watch; // 开始计时

string *pstr[5000];

for (int i=0;i<5000;i++)

{

pstr[i] = new string;

}

} // 摧毁计时器并报告结果

根据上面的代码段,当代码开始执行时,计时也开始,当代码退出时,析构函数便显示结果:

此操作所用时间: 27

转换成秒数: 0.027

循环在运行这段代码的机器上耗时27毫秒。现在对上面的代码段稍做改动,使用栈动态分配内存会得到什么样的性能数据呢?

{

stopwatch watch;

for (int i=0;i<5000;i++)

{

string s;//创建并销毁本地的自动创建的串

}

}

这段代码运行结果为:

此操作所用时间: 14

转换成秒数: 0.014

可以看出,用栈代替堆分配内存速度提高了50%。而且使用堆内存的代码还不包括销毁5000个串所用的时间。使用栈内存的代码不存在这个问题。由此很容易看出性能差别。

另外,使用堆内存的代码还有5000个赋值操作:

pstr[i] = new string;

将代码改动一下:

{

stopwatch watch;

for (int i=0;i<5000;i++)

{

new string; // 不用赋值的堆内存分配

}

}

通常的代码是不能这样写的-原因是这样的代码造成严重的内存溢出。但它把分配操作与其它的变量隔离开了。这段代码不是以赋值方式进行堆内存分配,这是性能调整时常用的方法,其运行结果如下:

此操作所用时间: 27

转换成秒数: 0.027

也就是说赋值不影响性能。

性能测试常常需要一些技术实践。开发人员的直觉常会令人误入歧途-直观上开销很大的操作往往对性能影响不大,而一些表面上无所谓的操作象动态内存分配证明了在内存开销上对CPU的依赖。所以说如果没有可靠的性能测试作为手段,我们是很难发现性能事实的。

原文转自:www.ltesting.net

篇7:浅析.NET开发中代理模式的使用.net

当我们需要使用的对象很复杂或者需要很长时间去构造,这时就可以使用代理模式(Proxy),例如:如果构建一个对象很耗费时间和计算机资源,代理模式(Proxy)允许我们控制这种情况,直到我们需要使用实际的对象。一个代理(Proxy)通常包含和将要使用的对象同样的方

当我们需要使用的对象很复杂或者需要很长时间去构造,这时就可以使用代理模式(Proxy)。例如:如果构建一个对象很耗费时间和计算机资源,代理模式(Proxy)允许我们控制这种情况,直到我们需要使用实际的对象。一个代理(Proxy)通常包含和将要使用的对象同样的方法,一旦开始使用这个对象,这些方法将通过代理(Proxy)传递给实际的对象。

一些可以使用代理模式(Proxy)的情况:

1、一个对象,比如一幅很大的图像,需要载入的时间很长。

2、一个需要很长时间才可以完成的计算结果,并且需要在它计算过程中显示中间结果

3、一个存在于远程计算机上的对象,需要通过网络载入这个远程对象则需要很长时间,特别是在网络传输高峰期。

4、一个对象只有有限的访问权限,代理模式(Proxy)可以验证用户的权限

代理模式(Proxy)也可以被用来区别一个对象实例的请求和实际的访问,例如:在程序初始化过程中可能建立多个对象,但并不都是马上使用,代理模式(Proxy)可以载入需要的真正的对象。

这是一个需要载入和显示一幅很大的图像的程序,当程序启动时,就必须确定要显示的图像,但是实际的图像只能在完全载入后才可以显示!这时我们就可以使用代理模式(Proxy)。

这个代理模式(Proxy)可以延迟实际图像的载入,直到它接收到一个paint请求。在实际图像的载入期间我们可以通过代理模式(Proxy)在实际图像要显示的位置预先载入一个比较小、简单的图形。

图像Proxy代码:

Public Class ImageProxy

Private done As Boolean

Private tm As Timer

Public Sub New()

done = False

'设置timer 延迟5秒

tm = New Timer(New TimerCallback(AddressOf tCallback), Me, 5000, 0)

End Sub

Public Function isReady() As Boolean

Return done

End Function

Public Function getImage() As Image

Dim img As Imager

'显示预先的图像,直到实际图像载入完成

If isReady Then

img = New FinalImage()

Else

img = New QuickImage()

End If

Return img.getImage

End Function

Public Sub tCallback(ByVal obj As Object)

done = True

tm.Dispose()

End Sub

End Class

定义一个简单的接口:

Public Interface Imager

Function getImage() As image

End Interface

实现接口:

预先载入的图像的类:

Public Class QuickImage

Implements Imager

Public Function getImage() As Image Implements Imager.getImage

Return New bitmap(”Box.gif“)

End Function

End Class

载入实际图像的类:

Public Class FinalImage

Implements Imager

Public Function getImage() As Image Implements Imager.getImage

Return New Bitmap(”flowrtree.jpg")

End Function

End Class

在显示图像的窗体中,定义一个图像代理的(Proxy)实例,在载入图像按钮事件中,载入图像:

Private imgProxy As ImageProxy

Public Sub New()

MyBase.New

Form1 = Me

InitializeComponent

imgproxy = New ImageProxy()

End Sub

Protected Sub btLoad_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btLoad.Click

pic.Image = imgProxy.getImage

End Sub

总结:

这只是一个很简单的例子(例子来自于《c#设计模式》),通过这个例子可以对代理(Proxy)有初步的认识!Adapter模式和代理模式(Proxy)都是在对象间构造一个简单的层,

然而,Adapter模式向对象提供一个不同的接口,代理模式(Proxy)为对象提供相同的接口。

原文转自:www.ltesting.net

篇8:经验:ESBasic可复用的.NET类库层级结构缓存IHiberarchyCache

1.缘起:

从IMultiTree到IAgileMultiTree,一切进展得都不错,但是,还有改进的地方。多叉树的一个优点在于,根据指定的节点能够非常迅速地找到其所有的子节点。但是缺点在于,根据节点值的ID定位到目标节点不够快,因为需要对所有的节点进行遍历操作。当节点非常多、层次非常深时,这种定位操作可能会严重的影响效率。

我设计了层级结构缓存ESBasic.ObjectManagement.Cache.IHiberarchyCache来加速这种根据节点值ID定位节点的访问。所谓“层级结构”,就是类似我们在IMultiTree章节缘起中介绍的那个多叉树式的组织结构。

使用IHiberarchyCache可以使两种操作都足够快:一是根据ID找到目标对象,另一种是根据ID找到其所有下级对象。

IHiberarchyCache融合了IAgileMultiTree和Dictionary两种对象容器的优势,以达到对层级结构的高效缓存。

层级结构缓存的形象示意图如下:

从示意图可以看到,IHiberarchyCache内部是借助IAgileMultiTree和ISmartDictionaryCache来实现的。

2.适用场合:

如果你在使用IAgileMultiTree的同时,经常需要根据节点值ID来定位节点,而且希望这种定位非常迅速,那么你可以改用IHiberarchyCache。

3.设计思想与实现

所有希望能够存储在IHiberarchyCache中的节点值必须实现IHiberarchyVal接口,该接口从IMTreeVal继承,其定义如下:

public interface IHiberarchyVal : IMTreeVal

{

string SequenceCode { get; }

}

其相比于IMTreeVal增加了一个SequenceCode属性,以表明节点值在多叉树中的具体位置。

最初的IMultiTree对节点值是否有SequenceCode并没有任何要求,也就是说没有SequenceCode特性也可以正常使用IMultiTree。接着,IAgileMultiTree将SequenceCode作为一个设计参数纳入到核心机制中。到现在IHiberarchyCache,SequenceCode已经是节点值必须实现的一个属性了,否则,IHiberarchyCache将无法正常工作。这是一个逐渐强化SequenceCode作用的过程。

接下来我们来看IHiberarchyCache接口的定义:

public interface IHiberarchyCache where TVal : IHiberarchyVal

{

///

/// RootID 设置根节点的ID。

///

string RootID { set; }

///

/// SequenceCodeSplitter 节点路径(序列号)的分割符。

///

char SequenceCodeSplitter { get; set; }

IObjectRetriever ObjectRetriever { set; }

int Count { get; }

void Initialize;

///

/// Get 如果目标对象在缓存中不存在,则通过ObjectRetriever去提取。

///

TVal Get(string id);

///

/// HaveContained 缓存中是否一经包含了目标对象。

///

bool HaveContained(string id);

///

/// GetAllKeyListCopy 获取所有ID的列表的拷贝。

///

IList GetAllKeyListCopy();

///

/// GetAllValListCopy 获取所有的节点值列表的拷贝。

///

IList GetAllValListCopy();

///

/// GetChildrenOf 获取parentID的所有孩子节点的节点值列表。

///

IList GetChildrenOf(string parentID);

///

/// GetChildrenCount 获取parentID直接下级的个数。

///

int GetChildrenCount(string parentID);

///

/// CreateHiberarchyTree 返回表示层级信息的最单纯的数据结构,

/// 注意:返回的Tree实际上与内部的AgileMultiTree是引用的根节点是同一个节点。

///

MultiTree CreateHiberarchyTree();

///

/// GetNodesOnDepthIndex 获取某一深度的所有节点。Root的深度索引为0

///

IList GetNodesOnDepthIndex(int depthIndex);

///

/// GetNodesOnDepthIndex 获取所属parentID体系下并且深度为depthIndex的所有节点。Root的深度索引为0

///

IList GetNodesOnDepthIndex(string parentID, int depthIndex);

}

RootID属性表明了根节点值的ID,IHiberarchyCache将会以该ID的节点值来初始化层级结构的根。

接下来的SequenceCodeSplitter、ObjectRetriever和Count属性的含义我们在前面介绍IMultiTree时已经详细介绍过了,这里就不再赘述。

以-Copy结尾的方法返回的都是对象集合的一个拷贝,在方法返回后,这个集合中的元素个数可以被修改,不会影响到IHiberarchyCache的内部缓存。

注意,GetChildrenOf方法返回的是TVal的IList,而不是MNode的IList。这是因为在IHiberarchyCache中,节点的概念已经被淡化了,它只是在IHiberarchyCache的内部实现时使用,用于加快类似获取某个元素的所有下级对象的访问。而外部根本不用在乎IHiberarchyCache内部的具体实现方式,所以不需要将MNode暴露出来。

而且,如果IHiberarchyCache的某个方法暴露出了MNode节点对象,则有可能产生危险,因为当用户获取了某MNode节点对象的引用后,就可以调用其AddChild方法手动向IHiberarchyCache内部多叉树中添加节点,而这个节点在内部的ISmartDictionaryCache缓存中并不存在,从而导致IHiberarchyCache内部的两个缓存的状态不一致。

CreateHiberarchyTree方法用于创建一个新的MultiTree,返回的MultiTree与IHiberarchyCache内部的多叉树拥有完全一样的结构。

GetNodesOnDepthIndex方法的含义与IMultiTree是完全一致的。

接下来我们将注意力转移到HiberarchyCache的具体实现上来。

正如示意图所展示的,HiberarchyCache内部使用了IAgileMultiTree和ISmartDictionaryCache,从而达到了我们在缘起部分希望达到的效果。但是图中还有一个细节我们应该注意到,那就是数据流的方向。我们看到,IAgileMultiTree的数据的来源是ISmartDictionaryCache,这意味着,如果目标对象在当前IHiberarchyCache实例中不存在,则会先通过IObjectRetriever将其加载到ISmartDictionaryCache中,然后IAgileMultiTree再从ISmartDictionaryCache中取出加载到多叉树中。那么,IAgileMultiTree是如何从ISmartDictionaryCache加载数据的了?是通过一个适配器,这个适配器叫做HiberarchyAgileNodePicker,其类图如下所示:

它将ISmartDictionaryCache适配为一个IAgileNodePicker对象给IAgileMultiTree使用。这一点我们可以从HiberarchyCache的Initialize方法的实现中一窥究竟。

关于HiberarchyCache的具体实现,其中的关键点罗列如下:

(1)关于线程安全的部分。由于内部仅有的两个容器ISmartDictionaryCache和IAgileMultiTree的实现都是线程安全的,所以HiberarchyCache本身也是线程安全的,而且不用做任何额外的处理。

(2)CreateHiberarchyTree方法返回的是内部多叉树的一个浅表拷贝。

(3)其它很多方法都是借助内部的IAgileMultiTree来完成的。

4. 使用时的注意事项

(1)  在介绍IMultiTree时,我们提到不要轻易调用其Count属性,因为每次调用都会进行一次递归统计动作,可能会影响性能。但是IHiberarchyCache的Count属性却可以非常快的返回,因为它直接返回了内部字典的Count属性,而不需要做额外的动作。

(2)   IHiberarchyCache加速了根据节点值ID定位节点的查找访问,这是通过使用额外的内存空间为代价的,即典型的“以空间换时间”的例子。幸运的是,需要重复存储的只是节点值ID,而不是整个节点值对象,所以这个额外的内存开销并不是特别大。在大多数情况下,这个开销是值得的。

(3)CreateHiberarchyTree方法返回的多叉树是只读的,这一点要特别注意。因为它只是IHiberarchyCache内部多叉树的一个浅表拷贝,所以对它的修改会导致IHiberarchyCache的内部状态不一致。通常,我使用CreateHiberarchyTree方法都是为了将返回的实例进行持久化存储的。

(4)     IHiberarchyCache内部使用的两个缓存容器IAgileMultiTree和ISmartDictionaryCache,它们的状态是完全一致的。也就是说,如果一个对象存在于ISmartDictionaryCache中,那么在IAgileMultiTree中一定就能找到与之对应的节点,反之亦然。

5.扩展

层级结构缓存IHiberarchyCache暂时没有任何扩展。

更多交流,让我们一起学习:

篇9:一个导出Excel非常快的类.net

unit DBGridEhToExcel; interface uses Windows, Messages, SysUtils, Vari ant s, Classes, Graphics, Controls, Forms, Dialogs, DB, ComCtrls, ExtCtrls, S td Ctrls, Gauges, DBGridEh, ShellApi; type TTitleCell = array of array of String; //分解DB

unit DBGridEhToExcel;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, DB, ComCtrls, ExtCtrls, StdCtrls, Gauges, DBGridEh, ShellApi;

type

TTitleCell = array of array of String;

//分解DBGridEh的标题

TDBGridEhTitle = class

private

FDBGridEh: TDBGridEh; //对应DBGridEh

FColumnCount: integer; //DBGridEh列数(指visible为True的列数)

FRowCount: integer;   //DBGridEh多表头层数(没有多表头则层数为1)

procedure SetDBGridEh(const Value: TDBGridEh);

function GetTitleRow: integer;   //获取DBGridEh多表头层数

function GetTitleColumn: integer; //获取DBGridEh列数

public

//分解DBGridEh标题,由TitleCell二维动态数组返回

procedure GetTitleData(var TitleCell: TTitleCell);

published

property DBGridEh: TDBGridEh read FDBGridEh write SetDBGridEh;

property ColumnCount: integer read FColumnCount;

property RowCount: integer read FRowCount;

end;

TDBGridEhToExcel = class(TComponent)

private

FCol: integer;

FRow: integer;

FProgressForm. TForm;                                 {进度窗体}

FGauge: TGauge;                                       {进度条}

Stream: TStream;                                      {输出文件流}

FBookMark: TBookmark;

FShowProgress: Boolean;                               {是否显示进度窗体}

FDBGridEh: TDBGridEh;

FBeginDate: TCaption;                                 {开始日期}

FTitleName: TCaption;                                 {Excel文件标题}

FEndDate: TCaption;                                   {结束日期}

FUserName: TCaption;                                  {制表人}

FFileName: String;                                    {保存文件名}

procedure SetShowProgress(const Value: Boolean);

procedure SetDBGridEh(const Value: TDBGridEh);

procedure SetBeginDate(const Value: TCaption);

procedure SetEndDate(const Value: TCaption);

procedure SetTitleName(const Value: TCaption);

procedure SetUserName(const Value: TCaption);

procedure SetFileName(const Value: String);

procedure IncColRow;

procedure WriteBlankCell;                             {写空单元格}

{写数字单元格}

procedure WriteFloatCell(const AValue: Double; const IncStatus: Boolean=True);

{写整型单元格}

procedure WriteIntegerCell(const AValue: Integer; const IncStatus: Boolean=True);

{写字符单元格}

procedure WriteStringCell(const AValue: string; const IncStatus: Boolean=True);

procedure WritePrefix;

procedure WriteSuffix;

procedure WriteHeader;                                {输出Excel标题}

procedure WriteTitle;                                 {输出Excel列标题}

procedure WriteDataCell;                              {输出数据集内容}

procedure WriteFooter;                                {输出DBGridEh表脚}

procedure SaveStream(aStream: TStream);

procedure CreateProcessForm(AOwner: TComponent);      {生成进度窗体}

{根据表格修改数据集字段顺序及字段中文标题}

procedure SetDataSetCrossIndexDBGridEh;

public

constructor Create(AOwner: TComponent); override;

destructor Destroy; override;

procedure ExportToExcel; {输出Excel文件}

published

property DBGridEh: TDBGridEh read FDBGridEh write SetDBGridEh;

property ShowProgress: Boolean read FShowProgress write SetShowProgress;

property TitleName: TCaption read FTitleName write SetTitleName;

property BeginDate: TCaption read FBeginDate write SetBeginDate;

property EndDate: TCaption read FEndDate write SetEndDate;

property UserName: TCaption read FUserName write SetUserName;

property FileName: String read FFileName write SetFileName;

end;

var

CXlsBof: array[0..5] of Word = ($809, 8, 0, $10, 0, 0);

CXlsEof: array[0..1] of Word = ($0A, 00);

CXlsLabel: array[0..5] of Word = ($204, 0, 0, 0, 0, 0);

CXlsNumber: array[0..4] of Word = ($203, 14, 0, 0, 0);

CXlsRk: array[0..4] of Word = ($27E, 10, 0, 0, 0);

CXlsBlank: array[0..4] of Word = ($201, 6, 0, 0, $17);

implementation

{ TDBGridEhTitle }

function TDBGridEhTitle.GetTitleColumn: integer;

var

i, ColumnCount: integer;

begin

ColumnCount := 0;

for i := 0 to DBGridEh.Columns.Count - 1 do

begin

if DBGridEh.Columns[i].Visible then

Inc(ColumnCount);

end;

Result := ColumnCount;

end;

procedure TDBGridEhTitle.GetTitleData(var TitleCell: TTitleCell);

var

i, Row, Col: integer;

Caption: String;

begin

FColumnCount := GetTitleColumn;

FRowCount := GetTitleRow;

SetLength(TitleCell,FColumnCount,FRowCount);

Row := 0;

for i := 0 to DBGridEh.Columns.Count - 1 do

begin

if DBGridEh.Columns[i].Visible then

begin

Col := 0;

Caption := DBGridEh.Columns[i].Title.Caption;

while POS('|', Caption) >0 do

begin

TitleCell[Row,Col] := Copy(Caption, 1, Pos('|',Caption)-1);

Caption := Copy(Caption,Pos('|', Caption)+1, Length(Caption));

Inc(Col);

end;

TitleCell[Row, Col] := Caption;

Inc(Row);

end;

end;

end;

function TDBGridEhTitle.GetTitleRow: integer;

var

i, j: integer;

MaxRow, Row: integer;

begin

MaxRow := 1;

for i := 0 to DBGridEh.Columns.Count - 1 do

begin

Row := 1;

for j := 0 to Length(DBGridEh.Columns[i].Title.Caption) do

begin

if DBGridEh.Columns[i].Title.Caption[j] = '|' then

Inc(Row);

end;

if MaxRow < Row then

MaxRow := Row;

end;

Result := MaxRow;

end;

procedure TDBGridEhTitle.SetDBGridEh(const Value: TDBGridEh);

begin

FDBGridEh := Value;

end;

{ TDBGridEhToExcel }

constructor TDBGridEhToExcel.Create(AOwner: TComponent);

begin

inherited Create(AOwner);

FShowProgress := True;

end;

procedure TDBGridEhToExcel.SetShowProgress(const Value: Boolean);

begin

FShowProgress := Value;

end;

procedure TDBGridEhToExcel.SetDBGridEh(const Value: TDBGridEh);

begin

FDBGridEh := Value;

end;

procedure TDBGridEhToExcel.SetBeginDate(const Value: TCaption);

begin

FBeginDate := Value;

end;

procedure TDBGridEhToExcel.SetEndDate(const Value: TCaption);

begin

FEndDate := Value;

end;

procedure TDBGridEhToExcel.SetTitleName(const Value: TCaption);

begin

FTitleName := Value;

end;

procedure TDBGridEhToExcel.SetUserName(const Value: TCaption);

begin

FUserName := Value;

end;

procedure TDBGridEhToExcel.SetFileName(const Value: String);

begin

FFileName := Value;

end;

procedure TDBGridEhToExcel.IncColRow;

begin

if FCol = DBGridEh.DataSource.DataSet.FieldCount - 1 then

begin

Inc(FRow);

FCol := 0;

end

else

Inc(FCol);

end;

procedure TDBGridEhToExcel.WriteBlankCell;

begin

CXlsBlank[2] := FRow;

CXlsBlank[3] := FCol;

Stream.WriteBuffer(CXlsBlank, SizeOf(CXlsBlank));

IncColRow;

end;

procedure TDBGridEhToExcel.WriteFloatCell(const AValue: Double; const IncStatus: Boolean=True);

begin

CXlsNumber[2] := FRow;

CXlsNumber[3] := FCol;

Stream.WriteBuffer(CXlsNumber, SizeOf(CXlsNumber));

Stream.WriteBuffer(AValue, 8);

if IncStatus then

IncColRow;

end;

procedure TDBGridEhToExcel.WriteIntegerCell(const AValue: Integer; const IncStatus: Boolean=True);

var

V: Integer;

begin

CXlsRk[2] := FRow;

CXlsRk[3] := FCol;

Stream.WriteBuffer(CXlsRk, SizeOf(CXlsRk));

V := (AValue Shl 2) Or 2;

Stream.WriteBuffer(V, 4);

if IncStatus then

IncColRow;

end;

procedure TDBGridEhToExcel.WriteStringCell(const AValue: string; const IncStatus: Boolean=True);

var

L: integer;

begin

L := Length(AValue);

CXlsLabel[1] := 8 + L;

CXlsLabel[2] := FRow;

CXlsLabel[3] := FCol;

CXlsLabel[5] := L;

Stream.WriteBuffer(CXlsLabel, SizeOf(CXlsLabel));

Stream.WriteBuffer(Pointer(AValue)^, L);

if IncStatus then

IncColRow;

end;

procedure TDBGridEhToExcel.WritePrefix;

begin

Stream.WriteBuffer(CXlsBof, SizeOf(CXlsBof));

end;

procedure TDBGridEhToExcel.WriteSuffix;

begin

Stream.WriteBuffer(CXlsEof, SizeOf(CXlsEof));

end;

procedure TDBGridEhToExcel.WriteHeader;

var

OpName, OpDate: String;

begin

//标题

FCol := 3;

WriteStringCell(TitleName,False);

FCol := 0;

Inc(FRow);

if Trim(BeginDate) '' then

begin

//开始日期

FCol := 0;

WriteStringCell(BeginDate,False);

FCol := 0

end;

if Trim(EndDate) '' then

begin

//结束日期

FCol := 5;

WriteStringCell(EndDate,False);

FCol := 0;

end;

if (Trim(BeginDate) '') or (Trim(EndDate) '') then

Inc(FRow);

//制表人

OpName := '制表人:' + UserName;

FCol := 0;

WriteStringCell(OpName,False);

FCol := 0;

//制表时间

OpDate := '制表时间:' + DateTimeToStr(Now);

FCol := 5;

WriteStringCell(OpDate,False);

FCol := 0;

Inc(FRow);

end;

procedure TDBGridEhToExcel.WriteTitle;

var

i, j: integer;

DBGridEhTitle: TDBGridEhTitle;

TitleCell: TTitleCell;

begin

DBGridEhTitle := TDBGridEhTitle.Create;

try

DBGridEhTitle.DBGridEh := FDBGridEh;

DBGridEhTitle.GetTitleData(TitleCell);

try

for i := 0 to DBGridEhTitle.RowCount - 1 do

begin

for j := 0 to DBGridEhTitle.ColumnCount - 1 do

begin

FCol := j;

WriteStringCell(TitleCell[j,i],False);

end;

Inc(FRow);

end;

FCol := 0;

except

end;

finally

DBGridEhTitle.Free;

end;

end;

procedure TDBGridEhToExcel.WriteDataCell;

var

i: integer;

begin

DBGridEh.DataSource.DataSet.DisableControls;

FBookMark := DBGridEh.DataSource.DataSet.GetBookmark;

try

DBGridEh.DataSource.DataSet.First;

while not DBGridEh.DataSource.DataSet.Eof do

begin

for i := 0 to DBGridEh.DataSource.DataSet.FieldCount - 1 do

begin

if DBGridEh.DataSource.DataSet.Fields[i].IsNull or (not DBGridEh.DataSource.DataSet.Fields[i].Visible) then

WriteBlankCell

else

begin

case DBGridEh.DataSource.DataSet.Fields[i].DataType of

ftSmallint, ftInteger, ftWord, ftAutoInc, ftBytes:

WriteIntegerCell(DBGridEh.DataSource.DataSet.Fields[i].AsInteger);

ftFloat, ftCurrency, ftBCD:

WriteFloatCell(DBGridEh.DataSource.DataSet.Fields[i].AsFloat);

else

if DBGridEh.DataSource.DataSet.Fields[i] Is TBlobfield then // 此类型的字段(图像等)暂无法读取显示

WriteStringCell('')

else

WriteStringCell(DBGridEh.DataSource.DataSet.Fields[i].AsString);

end;

end;

end;

//显示进度条进度过程

if ShowProgress then

begin

FGauge.Progress := DBGridEh.DataSource.DataSet.RecNo;

FGauge.Refresh;

end;

DBGridEh.DataSource.DataSet.Next;

end;

finally

if DBGridEh.DataSource.DataSet.BookmarkValid(FBookMark) then

DBGridEh.DataSource.DataSet.GotoBookmark(FBookMark);

DBGridEh.DataSource.DataSet.EnableControls;

end;

end;

procedure TDBGridEhToExcel.WriteFooter;

var

i, j: integer;

begin

if DBGridEh.FooterRowCount = 0 then exit;

FCol := 0;

if DBGridEh.FooterRowCount = 1 then

begin

for i := 0 to DBGridEh.Columns.Count - 1 do

begin

if DBGridEh.Columns[i].Visible then

begin

WriteStringCell(DBGridEh.Columns[i].Footer.Value,False);

Inc(FCol);

end;

end;

end

else if DBGridEh.FooterRowCount >1 then

begin

for i := 0 to DBGridEh.Columns.Count - 1 do

begin

if DBGridEh.Columns[i].Visible then

begin

for j := 0 to DBGridEh.Columns[i].Footers.Count - 1 do

begin

WriteStringCell(DBGridEh.Columns[i].Footers[j].Value ,False);

Inc(FRow);

end;

Inc(FCol);

FRow := FRow - DBGridEh.Columns[i].Footers.Count;

end;

end;

end;

FCol := 0;

end;

procedure TDBGridEhToExcel.SaveStream(aStream: TStream);

begin

FCol := 0;

FRow := 0;

Stream := aStream;

//输出前缀

WritePrefix;

//输出表格标题

WriteHeader;

//输出列标题

WriteTitle;

//输出数据集内容

WriteDataCell;

//输出DBGridEh表脚

WriteFooter;

//输出后缀

WriteSuffix;

end;

procedure TDBGridEhToExcel.ExportToExcel;

var

FileStream: TFileStream;

Msg: String;

begin

//如果数据集为空或没有打开则退出

if (DBGridEh.DataSource.DataSet.IsEmpty) or (not DBGridEh.DataSource.DataSet.Active) then

exit;

//如果保存的文件名为空则退出

if Trim(FileName) = '' then

exit;

//根据表格修改数据集字段顺序及字段中文标题

SetDataSetCrossIndexDBGridEh;

Screen.Cursor := crHourGlass;

try

try

if FileExists(FileName) then

begin

Msg := '已存在文件(' + FileName + '),是否覆盖?';

if Application.MessageBox(PChar(Msg),'提示',MB_YESNO+MB_ICONQUESTION+MB_DEFBUTTON2) = IDYES then

begin

//删除文件

DeleteFile(FileName)

end

else

exit;

end;

//显示进度窗体

if ShowProgress then

CreateProcessForm(nil);

FileStream := TFileStream.Create(FileName, fmCreate);

try

//输出文件

SaveStream(FileStream);

finally

FileStream.Free;

end;

//打开Excel文件

ShellExecute(0, 'Open', PChar(FileName), nil, nil, SW_SHOW);

except

end;

finally

if ShowProgress then

FreeAndNil(FProgressForm);

Screen.Cursor := crDefault;

end;

end;

destructor TDBGridEhToExcel.Destroy;

begin

inherited Destroy;

end;

procedure TDBGridEhToExcel.CreateProcessForm(AOwner: TComponent);

var

Panel: TPanel;

Prompt: TLabel;                                          {提示的标签}

begin

if Assigned(FProgressForm) then

exit;

FProgressForm.:= TForm.Create(AOwner);

with FProgressForm. do

begin

try

Font.Name := '宋体';                                 {设置字体}

Font.Size := 9;

BorderStyle.:= bsNone;

Width := 300;

Height := 100;

BorderWidth := 1;

Color := clBlack;

Position := poScreenCenter;

Panel := TPanel.Create(FProgressForm);

with Panel do

begin

Parent := FProgressForm;

Align := alClient;

BevelInner := bvNone;

BevelOuter := bvRaised;

Caption := '';

end;

Prompt := TLabel.Create(Panel);

with Prompt do

begin

Parent := Panel;

AutoSize := True;

Left := 25;

Top := 25;

Caption := '正在导出数据,请稍候......';

Font.Style.:= [fsBold];

end;

FGauge := TGauge.Create(Panel);

with FGauge do

begin

Parent := Panel;

ForeColor := clBlue;

Left := 20;

Top := 50;

Height := 13;

Width := 260;

MinValue := 0;

MaxValue := DBGridEh.DataSource.DataSet.RecordCount;

end;

except

end;

end;

FProgressForm.Show;

FProgressForm.Update;

end;

procedure TDBGridEhToExcel.SetDataSetCrossIndexDBGridEh;

var

i: integer;

begin

for i := 0 to DBGridEh.Columns.Count - 1 do

begin

DBGridEh.DataSource.DataSet.FieldByName(DBGridEh.Columns.Items[i].FieldName).Index := i;

DBGridEh.DataSource.DataSet.FieldByName(DBGridEh.Columns.Items[i].FieldName).DisplayLabel

:= DBGridEh.Columns.Items[i].Title.Caption;

DBGridEh.DataSource.DataSet.FieldByName(DBGridEh.Columns.Items[i].FieldName).Visible :=

DBGridEh.Columns.Items[i].Visible;

end;

for i := 0 to DBGridEh.DataSource.DataSet.FieldCount - 1 do

begin

if POS('*****',DBGridEh.DataSource.DataSet.Fields[i].DisplayLabel) >0 then

DBGridEh.DataSource.DataSet.Fields[i].Visible := False;

end;

end;

end.

/*****************************************************************/

调用的例子

var

DBGridEhToExcel: TDBGridEhToExcel;

begin

DBGridEhToExcel := TDBGridEhToExcel.Create(nil);

try

DBGridEhToExcel.TitleName := '测试测试测试测试测试测试测试';

DBGridEhToExcel.BeginDate := '开始日期:2005-07-01';

DBGridEhToExcel.EndDate := '结束日期:2005-07-18';

DBGridEhToExcel.UserName := '系统管理员';

DBGridEhToExcel.DBGridEh := DBGridEh1;

DBGridEhToExcel.ShowProgress := True;

DBGridEhToExcel.FileName := 'c:\123.xls';

DBGridEhToExcel.ExportToExcel;

finally

DBGridEhToExcel.Free;

end;

原文转自:www.ltesting.net

Linux内核分析 网络[十一]:ICMP模块

网络沟通的桥梁协议X档案(二)网络知识

java的学习计划

电气工程师面试题

有关后门的知识详解 武林安全网

网络最高安全指南Windows系统

关于精选php面试题及答案

网络经典命令行

java学习总结

nginx配置中文域名WEB服务器

.Net 缓存代理类 的初步考虑.net
《.Net 缓存代理类 的初步考虑.net.doc》
将本文的Word文档下载到电脑,方便收藏和打印
推荐度:
点击下载文档

【.Net 缓存代理类 的初步考虑.net(合集9篇)】相关文章:

一份比较全的PHP笔试题2024-02-25

ASP 3.0高级编程三2023-01-25

经验:网络基本故障一览表2022-11-18

DNS系列一:DNS查询原理2022-05-06

教菜鸟如何攻击139端口 武林安全网2023-07-27

网络协议:Ethereal协议分析系统介绍网络知识2023-01-17

淘宝招聘笔试真题2022-12-03

Windows NT4.0 OS repair网络服务器2022-04-30

《互联网软件应用于开发》教案11-12章2022-10-27

用Sygate4.0组建代理服务器!Windows系统2022-04-30

点击下载本文文档