您的位置:首页 > 移动开发

Microsoft .NET Framework 2.0 Application Development Foundation 翻译系列7(第一章:第三课 类的构造)

2007-01-03 11:00 666 查看

Lesson3:ConstructingClasses类的构造

Inobject-orientedlanguages,thebulkoftheworkshouldbeperformedwithinobjects.Allbutthesimplestapplicationsrequireconstructingoneormorecustomclasses,eachwithmultiplepropertiesandmethodsusedtoperformtasksrelatedtothatobject.Thislessondiscusseshowtocreatecustomclasses.

在面向对象的语言中,大部分的工作都是用对象来实现的。就连最简单的应用需求都会创建一个或更多的自定义类,而每个自定义类又包含了大量的属性和方法用来执行与对象有关的任务。本课就是讨论如何建立自定义类。

Afterthislesson,youwillbeableto:课后你将可以

·Describeanduseinheritance.描述和使用继承

·Describeanduseinterfaces.描述和使用接口

·Describeandusepartialclasses.描述和使用飞类(见Microsoft.NETFramework2.0ApplicationDevelopmentFoundation翻译系列2(第一章:框架基本原理)中Overview概述)

·Createagenerictype,andusethebuilt-ingenerictypes.建立一个泛型类型和使用net框架中自带(默认)的泛型类型。

·Respondtoandraiseevents.响应和触发事件

·Addattributestodescribeassembliesandmethods.添加用来描述程序集和方法的属性

·Moveatypefromoneclasslibrarytoanotherusingtypeforwarding.利用类型传递(见Microsoft.NETFramework2.0ApplicationDevelopmentFoundation翻译系列2(第一章:框架基本原理)中Overview概述),将一个类型从一个类库移动到另一个类库中。

Estimatedlessontime:40minutes课时40分钟

WhatIsInheritance?什么是继承

The.NETFrameworkhasthousandsofclasses,andeachclasshasmanydifferentmethodsandproperties.Keepingtrackofalltheseclassesandmemberswouldbeimpossibleifthe.NETFrameworkwerenotimplementedextremelyconsistently.Forexample,everyclasshasaToStringmethodthatperformsexactlythesametask—convertinganinstanceoftheclassintoastring.Similarly,manyclassessupportthesameoperators,suchascomparingtwoinstancesofaclassforequality.

在net框架中有数以千计的类,并且每个类都有许多不同的方法和属性。如果net框架无法保持始终如一,那么想要了解所有这些类和成员几乎是不可能的。举个例子来说,每个类都有一个ToString()方法用来正确的执行相同的任务-将一个类的实例转换为一个字符串。同样的,许多类都支持相同的操作,例如象比较两个类实例是否相等的操作。

Thisconsistencyispossiblebecauseofinheritanceandinterfaces(describedinthenextsection).Useinheritancetocreatenewclassesfromexistingones.Forexample,youwilllearninChapter6,"Graphics,"thattheBitmapclassinheritsfromtheImageclassandextendsitbyaddingfunctionality.Therefore,youcanuseaninstanceoftheBitmapclassinthesamewaysthatyouwoulduseaninstanceoftheImageclass.However,theBitmapclassprovidesadditionalmethodsthatenableyoutodomorewithpictures.

由于继承和接口的使用才使这种一致性成为可能(将在下一部分描述)。使用继承从一个已经存在的类来建立一个新的类。举例来说,你将会在第六章绘图中学到的Bitmap类就是从Image类继承并添加功能扩展而得来的。因此,你可以象使用Image类的实例一样,使用同样的方法来操作Bitmap类的实例。只不过,Bitmap类提供了额外的操作图片的方法。

YoucaneasilycreateacustomexceptionclassbyinheritingfromSystem.ApplicationException,asshownhere:

你可以容易的通过继承System.ApplicationException来建立一个自定义异常类。如下代码。

'VB

ClassDerivedException

InheritsSystem.ApplicationException


PublicOverridesReadOnlyPropertyMessage()AsString

Get

Return"Anerroroccurredintheapplication."

EndGet

EndProperty

EndClass


//C#

classDerivedException:System.ApplicationException

{


publicoverridestringMessage

{

get{return"Anerroroccurredintheapplication.";}

}

}

Youcanthrowandcatchthenewexceptionbecausethecustomclassinheritsthatbehaviorofitsbaseclass,asshownhere:

你可以抛出并捕获这个新的异常,因为这个自定义异常类继承了它基类的行为。

'VB

Try

ThrowNewDerivedException

CatchexAsDerivedException

Console.WriteLine("Source:{0},Error:{1}",ex.Source,ex.Message)

EndTry


//C#

try

{

thrownewDerivedException();

}

catch(DerivedExceptionex)

{

Console.WriteLine("Source:{0},Error:{1}",ex.Source,ex.Message);

}

Noticethatthecustomexceptionnotonlysupportsthethrow/catchbehavior,butitalsoincludesaSourcemember(aswellasothers)inheritedfromSystem.ApplicationException.

注意,这个自定义异常类不仅仅支持throw和catch行为,而且包含一个从System.ApplicationException继承来的Source成员(其它自定义异常类也都有)

Anotherbenefitofinheritanceistheabilitytousederivedclassesinterchangeably.Forexample,therearefiveclassesthatinheritfromtheSystem.Drawing.Brushbaseclass:HatchBrush,LinearGradientBrush,PathGradientBrush,SolidBrush,andTextureBrush.TheGraphics.DrawRectanglemethodrequiresaBrushobjectasoneofitsparameters;however,youwillneverpassthebaseBrushclasstoGraphics.DrawRectangle.Instead,youwillpassoneofthederivedclasses.BecausetheyareeachderivedfromtheBrushclass,theGraphics.DrawRectanglemethodcanacceptanyofthem.Similarly,ifyouweretocreateacustomclassderivedfromtheBrushclass,youcouldalsopassthatclasstoGraphics.DrawRectangle.

继承的另一个好处是它有能力可以代替它的父类(来源类)。举例来说,有五个类,它们继承于System.Drawing.Brush基类:HatchBrush,LinearGradientBrush,PathGradientBrush,SolidBrush,andTextureBrush。Graphics.DrawRectangle方法需要一个Brush对象作为参数,可是,你不能传递这个Brush基类给Graphics.DrawRectangle方法。那么,你可以传递继承了这个Brush基类的类中的一个。因为它们都来源于Brush类,这个Graphics.DrawRectangle方法可以接收它们中的任何一个。同样的,如果你建立了一个来源于Brush类的自定义类,你也可以将它们传递给Graphics.DrawRectangle方法。

WhatIsanInterface?什么是接口?

Interfaces,alsoknownascontracts,defineacommonsetofmembersthatallclassesthatimplementtheinterfacemustprovide.Forexample,theIComparableinterfacedefinestheCompareTomethod,whichenablestwoinstancesofaclasstobecomparedforequality.AllclassesthatimplementtheIComparableinterface,whethercustom-createdorbuiltinthe.NETFramework,canbecomparedforequality.

接口,也可以理解为“约束”,它定义一个公共成员集合,实现这个接口的所有类都要提供这个公共成员的集合。举个例子,IComparable接口定义了CompareTo方法,用来允许比较同一个类的两个实例是否相等。所有实现了IComparable接口的类,无论它是自定义的还是net框架自带的,都可以对它们进行比较。

IDisposableisaninterfacethatprovidesasinglemethod,Dispose,toenableassembliesthatcreateaninstanceofyourclasstofreeupanyresourcestheinstancehasconsumed.TocreateaclassthatimplementstheIDisposableinterfaceusingVisualStudio2005,followthesesteps:

IDisposable提供了一个独有的方法Dispose,这个方法用来允许你建立的类实例的程序集去释放任何被实例占用的资源。下面示范使用VS2005建立一个实现了IDisposable接口的类,步骤如下:

1.Createtheclassdeclaration.Forexample:建立这个类的声明

2.'VB

3.ClassBigClass

4.EndClass

5.

6.//C#

7.classBigClass

8.{

9.}

10.Addtheinterfacedeclaration.Forexample:添加接口的声明

11.'VB

12.ClassBigClass

13.ImplementsIDisposable

14.EndClass

15.

16.//C#

17.classBigClass:IDisposable

18.{

19.}

20.IfyouareusingVisualBasic,VisualStudioshouldautomaticallygeneratemethoddeclarationsforeachoftherequiredmethods.Ifitdoesnot,deletetheImplementscommandandtryagain;VisualStudiomaystillbestartingup.IfyouareusingC#,right-clicktheInterfacedeclaration,clickImplementInterface,andthenclickImplementInterfaceagain,asshowninFigure1-1.

如果你是使用VB,VS将自动建立实现接口需要的每一个方法声明。如果VS没有自动建立,请删除实现接口的命令并重试。VS有可能还在初始化。如果你用的是C#,右键点击这个接口的声明,点击“实现接口”菜单项,然后,再点击“实现接口”子项,如图1-1



图1-1:VisualStudiosimplifiesimplementinganinterface

21.Writecodeforeachoftheinterface'smethods.Inthisexample,youwouldwritecodeintheDisposemethodtodeallocateanyresourcesyouhadallocated.

为接口的每个方法编写代码。在这个例子中,你需要在Dispost方法中编写代码,用来释放你曾经分配过的任何资源。

Table1-6liststhemostcommonlyusedinterfacesinthe.NETFramework.

表1-6列出了大部分在net框架中经常使用的接口

Table1-6:Commonlyusedinterfaces常用接口

Class

Description

IComparable

Implementedbytypeswhosevaluescanbeordered;forexample,thenumericandstringclasses.IComparableisrequiredforsorting.

通过类型来实现哪些值能够被排序;例如,数字和string类。IComparable是用来存储数据所必须的。

IDisposable

Definesmethodsformanuallydisposingofanobject.Thisinterfaceisimportantforlargeobjectsthatconsumeresources,orobjectsthatlockaccesstoresourcessuchasdatabases.

定义手动释放一个对象的方法。这个接口对于很多消耗资源的对象或者象数据库这样的独占资源的对象都非常重要。

IConvertible

EnablesaclasstobeconvertedtoabasetypesuchasBoolean,Byte,Double,orString.

允许将一个类转换为一个基本类型,例如boolean,byte,double,或者string。

ICloneable

Supportscopyinganobject.为对象的复制提供支持

IEquatable

Allowsyoutocomparetoinstancesofaclassforequality.Forexample,ifyouimplementthisinterface,youcouldsay"if(a==b)"。

比较类实例是否相等。例如,假设你实现了这个接口,你可以这样做"if(a==b)"。

IFormattable

Enablesyoutoconvertthevalueofanobjectintoaspeciallyformattedstring.ThisprovidesgreaterflexibilitythanthebaseToStringmethod.

允许你将一个对象的值转换为一个指定格式的字符串。与ToString()方法相比,它提供了更大的灵活性。

Youcancreateyourowninterfaces,too.Thisisusefulifyouneedtocreatemultiplecustomclassesthatbehavesimilarlyandcanbeusedinterchangeably.Forexample,thefollowingcodedefinesaninterfacecontainingthreemembers:

你也可以建立你自己的接口。当你要建立多个具有能够使用的、相似功能的自定义类时,这对于你是非常有用的。例如,下面代码定义一个接口,这个接口包含了三个成员。

'VB

InterfaceIMessage

'Sendthemessage.ReturnsTrueissuccess,Falseotherwise.

FunctionSend()AsBoolean

'Themessagetosend.

PropertyMessage()AsString

'TheAddresstosendto.

PropertyAddress()AsString

EndInterface


//C#

interfaceIMessage

{


//Sendthemessage.ReturnsTrueissuccess,Falseotherwise.

boolSend();

//Themessagetosend.

stringMessage{get;set;}

//TheAddresstosendto.

stringAddress{get;set;}

}

Ifyouimplementthatinterfaceinanewclass,VisualStudiogeneratesthefollowingtemplatefortheinterfacemembers:

如果你在一个新的类中实现这个接口,VS将自动生成这个接口成员的模板代码块。

'VB

ClassEmailMessage

ImplementsIMessage


PublicPropertyAddress()AsStringImplementsIMessage.Address

Get

EndGet

Set(ByValvalueAsString)

EndSet

EndProperty


PublicPropertyMessage()AsStringImplementsIMessage.Message

Get

EndGet

Set(ByValvalueAsString)

EndSet

EndProperty


PublicFunctionSend()AsBooleanImplementsIMessage.Send

EndFunction

EndClass


//C#

classEmailMessage:IMessage

{

publicboolSend()

{

thrownewException("Themethodoroperationisnotimplemented.");

}


publicstringMessage

{

get

{

thrownewException("Themethodoroperationisnotimplemented.");

}

set

{

thrownewException("Themethodoroperationisnotimplemented.");

}

}


publicstringAddress

{

get

{

thrownewException("Themethodoroperationisnotimplemented.");

}

set

{

thrownewException("Themethodoroperationisnotimplemented.");

}

}

}

Ifyoucreateacustomclassandlaterdecidethatitwouldbeusefultohavemultipleclasseswiththesamemembers,VisualStudiohasashortcuttoextractaninterfacefromacustomclass.Simplyfollowthesesteps:

如果你建立一个自定义类,并随后决定让它具有与多个类相同的成员,VS有一个快捷的方法,用来从一个自定义类中提取出一个接口。只需下面这些简单的步骤。

1.Right-clicktheclassinVisualStudio2005.在VS2005中右键点击这个类。

2.ClickRefactorandthenclickExtractInterface.点击“Refactor”然后再点击“ExtractInterface”

3.Specifytheinterfacename,selectthepublicmembersthatshouldformtheinterface,andthenclickOK.指定这个接口的名称,选择公共成员后点击ok。

Classescanimplementmultipleinterfaces.Therefore,aclasscouldimplementboththeIComparableandIDisposableinterfaces.

类能够实现多个接口。因此,一个类能够将IComparable和IDisposable两个接口都实现。

WhatArePartialClasses?什么是飞类

.NET2.0

Partialclassesarenewin.NET2.0.

只在net2.0中提供。

Partialclassesallowyoutosplitaclassdefinitionacrossmultiplesourcefiles.Thebenefitofthisapproachisthatithidesdetailsoftheclassdefinitionsothatderivedclassescanfocusonmoresignificantportions.

飞类允许你将一个类的定义进行分割,并将各个部分放到多个源文件中。这种方法的好处是,它将类定义的详细定义隐藏起来,因此可以使你关注更重要的部分。

TheWindowsFormclassisanexampleofabuilt-inpartialclass.InVisualStudio2003andearlier,formsclassesincludedcodegeneratedbytheformdesigner.Nowthatcodeishiddeninapartialclassnamedform.Designer.vborform.Designer.cs.

WindowForm类就是一个net自带的飞类的例子。在vs2003或更早的版本中,forms类通过form设计器生成代码。现在,代码则隐藏在form.Designer.vb或form.Designer.cs飞类中。

InVisualBasic,youmustselectShowAllFilesintheSolutionExplorertoseethepartialclassfiles.InC#,thatviewisenabledbydefault.Partialclassesaren'tpartoftheexamobjectives,butyouneedtoknowaboutthemsothatyoucanfindtheFormDesignercodewhenyoucreateanewWindowsForm.

在VB中,你必须在解决方案管理器中选择显示所有文件功能后,才能看到飞类文件。而在C#中,这是默认显示的。飞类不属于考试内容,但是你需要知道它们。这样你才能在建立一个新的WindowForm时找到这个Form设计器代码。

WhatAreGenerics?什么是泛型?

Genericsarepartofthe.NETFramework'stypesystemthatallowsyoutodefineatypewhileleavingsomedetailsunspecified.Insteadofspecifyingthetypesofparametersormemberclasses,youcanallowcodethatusesyourtypetospecifyit.Thisallowsconsumercodetotailoryourtypetoitsownspecificneeds.

泛型是.net框架类型系统的一部分,它允许你去定义一个不具体指定的类型。然后,在代码中,再用那些指定了类型的参数或成员的类,替换你定义的这个没有具体指定的类型。这允许实例代码将这个类型转换为它需要的类型。

ExamTip

Generictypesarenewin.NET2.0,andyouwillprobablyseeanunusuallylargenumberofquestionsaboutgenericsontheexam.

泛型在net2.0中是新的类型,在这个考试中,你也许会遇到大量的、不常见的关于泛型的问题。

The.NETFrameworkversion2.0includesseveralgenericclassesintheSystem.Collections.Genericnamespace,includingDictionary,Queue,SortedDictionary,andSortedList.TheseclassesworksimilarlytotheirnongenericcounterpartsinSystem.Collections,buttheyofferimprovedperformanceandtypesafety.

Net框架2.0版本中,在System.Collections.Generic命名空间包含了几个泛型类,包括Dictionary,Queue,SortedDictionary,和SortedList。这些类和在System.Collections命名空间中的那些与它们同名的类具有同样的功能。但是,它们提供了性能改善和类型安全。

MoreInfo—Genericcollections

The.NETFrameworkversion2.0includestheSystem.Collections.Genericnamespace,whichprovidesbuilt-incollectionsthatofferimprovedperformanceoverstandardcollections.Formoreinformation,refertoChapter4,"CollectionsandGenerics."

System.Collections.Generic命名空间中的Collections(连接集)比标准Collections提供了更好的性能。查看更多信息,参考第四章“Collections(连接集)和泛型”

WhyUseGenerics?为什么要使用泛型?

Versions1.0and1.1ofthe.NETFrameworkdidnotsupportgenerics.Instead,developersusedtheObjectclassforparametersandmembersandwouldcastotherclassestoandfromtheObjectclass.GenericsoffertwosignificantadvantagesoverusingtheObjectclass:

在net框架1.0和1.1中是不支持泛型的。代替方案是,开发人员使用Object类型的参数和成员,并将它们赋给其它类型,或者获取Object类型。泛型提供了比Object类更好的两点重要的优势。

·Reducedrun-timeerrorsThecompilercannotdetecttypeerrorswhenyoucasttoandfromtheObjectclass.Forexample,ifyoucastastringtoanObjectclassandthenattempttocastthatObjecttoaninteger,thecompilerwillnotcatchtheerror.Instead,theruntimewillthrowanexception.Usinggenericsallowsthecompilertocatchthistypeofbugbeforeyourprogramruns.Additionally,youcanspecifyconstraintstolimittheclassesusedinageneric,enablingthecompilertodetectanincompatibletype.

·简化运行时错误当你传递或者获取Object类时,编译器是无法识别类型错误的。举个例子,如果你将一个字符串赋给一个Object类,然后,尝试将这个Object类赋给一个整数,编译器不会捕获到错误。但在运行时将会抛出一个异常。而使用泛型,编译器就能在程序运行前捕获这个错误类型。更好的是,在一个泛型中,你可以约束类型匹配,也就是说,这种约束允许编译器能够识别不匹配的类型。

·ImprovedperformanceCastingrequiresboxingandunboxing(explainedlaterinLesson4,"ConvertingBetweenTypes"),whichstealsprocessortimeandslowsperformance.Usinggenericsdoesn'trequirecastingorboxing,whichimprovesrun-timeperformance.

·性能优化造型(参考下面“造型”(Casting)的定义)会产生装箱和拆箱(稍后会在第四课“类型间的转换”中解释),这将会损耗处理器时间并且减慢性能。而使用泛型,就不需要造型或装箱过程,这改善了运行时的性能。

“造型”(Casting)的定义:

基本类型转换:比如说一个方法或者变量接受一个较小的类型作为参数,而实际传入一个较大的,在传入的时候,就必须做“类型转换”

如:

doubled=3.1415d;

inti=(int)3.1415d;

thinkinjava中称之为“窄化转换”(narrowingconversion)

以后还有遇到“类类型转换”也可以说“造型”(Casting)

·

RealWorld真实世界

TonyNorthrup

Ihaven'tbeenabletoreproducetheperformancebenefitsofgenerics;however,accordingtoMicrosoft,genericsarefasterthanusingcasting.Inpractice,castingprovedtobeseveraltimesfasterthanusingageneric.However,youprobablywon'tnoticeperformancedifferencesinyourapplications.(Mytestsover100,000iterationstookonlyafewseconds.)Soyoushouldstillusegenericsbecausetheyaretype-safe.

我不想再复述泛型对性能的好处;无论如何,按照微软的说法,泛型比造型要快。在实际中,虽然造型会有几次比泛型要快,可是,在你的应用程序中,你可能不会注意到性能上有什么不同。(我反复测试了超过100000次,这仅用了很少的时间。)所以,你应该坚持使用泛型,因为它们是类型安全的。

HowtoCreateaGenericType如何建立一个泛型类型?

First,examinethefollowingclasses.ClassesObjandGenperformexactlythesametasks,butObjusestheObjectclasstoenableanytypetobepassed,whileGenusesgenerics:

首先,看一下下面的类.Obj和Gen执行同样的任务,但是Obj使用Object类型来允许任意类型的传递,而Gen则使用泛型。

'VB

ClassObj

PublicV1AsObject

PublicV2AsObject


PublicSubNew(ByVal_V1AsObject,ByVal_V2AsObject)

V1=_V1

V2=_V2

EndSub

EndClass


ClassGen(ofT,U)

PublicV1AsT

PublicV2AsU


PublicSubNew(ByVal_V1AsT,ByVal_V2AsU)

V1=_V1

V2=_V2

EndSub

EndClass


//C#

classObj

{

publicObjectt;

publicObjectu;


publicObj(Object_t,Object_u)

{

t=_t;

u=_u;

}

}


classGen<T,U>

{

publicTt;

publicUu;


publicGen(T_t,U_u)

{

t=_t;

u=_u;

}

}

Asyoucansee,theObjclasshastwomembersoftypeObject.TheGenclasshastwomembersoftypeTandU.TheconsumingcodewilldeterminethetypesforTandU.DependingonhowtheconsumingcodeusestheGenclass,TandUcouldbeastring,anint,acustomclass,oranycombinationthereof.

象你看到的,这个Obj类有两个Object类型的成员。这个Gen类有两个分别为T和U类型的成员。而Gen类中的赋值代码段(Gen中加粗体+加斜体红色的部分。在Gen类中是它的构造函数)将决定T和U的类型,依靠这段赋值代码段可以使T和U转换为一个字符串,一个整数,一个自定义类,或者任何其他组合。

Thereisasignificantlimitationtocreatingagenericclass:genericcodeisvalidonlyifitwillcompileforeverypossibleconstructedinstanceofthegeneric,whetheranInt,astring,oranyotherclass.Essentially,youarelimitedtothecapabilitiesofthebaseObjectclasswhenwritinggenericcode.Therefore,youcouldcalltheToStringorGetHashCodemethodwithinyourclass,butyoucouldnotusethe+or>operator.Thesesamerestrictionsdonotapplytotheconsumingcodebecausetheconsumingcodehasdeclaredatypeforthegeneric.

在建立一个泛型类时有一个重要的局限性:就是泛型代码总是正确的。因为,无论是一个Int,一个string,或者任何其他类,只要构造的是泛型实例,那它就是可以编译通过的。原则上,当编写泛型代码时,你的类能实现的功能会受到Object基类的约束。所以,在你的类中,你可以调用ToString()或GetHashCode方法,却不能使用+或>操作符。但这些相同的约束不能应用到赋值代码段中,因为赋值代码段中存在了被声明为泛型的类型。

HowtoConsumeaGenericType如何实例化泛型

Whenyouconsumeagenerictype,youmustspecifythetypesforanygenericsused.Considerthefollowingconsoleapplicationcode,whichusestheGenandObjclasses:

当你实例化一个泛型类型时,你必须指定这个泛型类型要使用的具体类型。考虑下面代码是如何使用Gen和Obj类的。

'VB

'AddtwoStringsusingtheObjclass

DimoaAsObj=NewObj("Hello,","World!")

Console.WriteLine(CType(oa.V1,String)+CType(oa.V2,String))


'AddtwoStringsusingtheGenclass

DimgaAsNewGen(OfString,String)("Hello,","World!")

Console.WriteLine(ga.V1+ga.V2)


'AddaDoubleandanIntegerusingtheObjclass

DimobAsObj=NewObj(10.125,2005)

Console.WriteLine(CType(ob.V1,Double)+CType(ob.V2,Integer))


'AddaDoubleandanIntegerusingtheGenclass

DimgbAsNewGen(OfDouble,Integer)(10.125,2005)

Console.WriteLine(gb.V1+gb.V2)


//C#

//AddtwostringsusingtheObjclass

Objoa=newObj("Hello,","World!");

Console.WriteLine((string)oa.t+(string)oa.u);


//AddtwostringsusingtheGenclass

Gen<string,string>ga=newGen<string,string>("Hello,","World!");

Console.WriteLine(ga.t+ga.u);


//AddadoubleandanintusingtheObjclass

Objob=newObj(10.125,2005);

Console.WriteLine((double)ob.t+(int)ob.u);


//AddadoubleandanintusingtheGenclass

Gen<double,int>gb=newGen<double,int>(10.125,2005);

Console.WriteLine(gb.t+gb.u);

Ifyourunthatcodeinaconsoleapplication,theObjandGenclassesproduceexactlythesameresults.However,thecodethatusestheGenclassactuallyworksfasterbecauseitdoesnotrequireboxingandunboxingtoandfromtheObjectclass.Additionally,developerswouldhaveamucheasiertimeusingtheGenclass.First,developerswouldnothavetomanuallycastfromtheObjectclasstotheappropriatetypes.Second,typeerrorswouldbecaughtatcompiletimeratherthanatruntime.Todemonstratethatbenefit,considerthefollowingcode,whichcontainsanerror:

如果你运行上面这段代码,这个Obj和Gen类都输出了正确的、并且相同的结果。可是,Gen类的代码运行的更快些,因为它不需要装箱和拆箱过程。另外,开发人员使用Gen类会感觉非常轻松。首先,开发人员不需要手动传递Object给适当的类型。第二,类型错误将会在编译时就被捕获。考虑下面代码,它包含了一个错误,用来示范这个优点。

'VB

'AddaDoubleandanIntegerusingtheGenclass用一个double和一个integer实例化Gen类。

DimgbAsNewGen(OfDouble,Integer)(10.125,2005)

Console.WriteLine(gb.V1+gb.V2)


'AddaDoubleandanIntegerusingtheObjclass用一个double和一个integer实例化Obj类。

DimobAsObj=NewObj(10.125,2005)

Console.WriteLine(CType(ob.V1,Integer)+CType(ob.V2,Integer))


//C#

//AddadoubleandanintusingtheGenclass用一个double和一个integer实例化Gen类。

Gen<double,int>gc=newGen<double,int>(10.125,2005);

Console.WriteLine(gc.t+gc.u);


//AddadoubleandanintusingtheObjclass用一个double和一个integer实例化Obj类。

Objoc=newObj(10.125,2005);

Console.WriteLine((int)oc.t+(int)oc.u);

Thelastlineinthatcodesamplecontainsanerror—theoc.tvalueiscasttoanIntinsteadoftoadouble.Unfortunately,thecompilerwon'tcatchthemistake.Instead,inC#,arun-timeexceptionisthrownwhentheruntimeattemptstocastadoubletoanIntvalue.InVisualBasic,whichallowsnarrowingconversionsbydefault,theresultisevenworse-amiscalculationoccurs.It'smucheasiertofixabugthatthecompilercatchesandmuchhardertodetectandfixarun-timeerror,sothegenericclassprovidesaclearbenefit.

在上面例子代码的最后一行包含了一个错误-将oc.t的double值转换为int类型。不幸的是,编译器无法捕获这个错误。在C#中,只有当运行时试图去将一个double转换为int类型时,才会抛出运行时错误。而在VB中,默认是允许这种类型转换的,结果更糟糕,将会导致错误计算(计算结果有误差)。尽早的确定这个bug并在编译器中捕获它,与一个难以察觉并确定的运行时错误相比,泛型类提供了一个明确的好处。

HowtoUseConstraints如何使用约束

Genericswouldbeextremelylimitedifyoucouldonlywritecodethatwouldcompileforanyclass,becauseyouwouldbelimitedtothecapabilitiesofthebaseObjectclass.Toovercomethislimitation,useconstraintstoplacerequirementsonthetypesthatconsumingcodecansubstituteforyourgeneric.

假设你仅仅是编写类的编译代码的话,泛型将会受到强大的限制。因为,你只具有Object基类的功能。那么,如何克服这种限制呢,答案是使用“约束“。

Genericssupportfourtypesofconstraints:泛型支持四种约束类型。

·InterfaceAllowonlytypesthatimplementspecificinterfacestouseyourgeneric.

·接口约束只有指定接口或实现了指定接口的类才能作为参数实例化你的泛型。

·BaseclassAllowonlytypesthatmatchorinheritfromaspecificbaseclasstouseyourgeneric.

·基类约束只有指定基类或者继承于这个指定基类的类才能作为参数实例化你的泛型

·ConstructorRequiretypesthatuseyourgenerictoimplementaparameterlessconstructor.

·构造函数约束无参数的构造函数才能作为参数实例化你的泛型

·ReferenceorvaluetypeRequiretypesthatuseyourgenerictobeeitherareferenceorvaluetype.

·引用类型或值类型约束任何一个引用类型或值类型,均可作为参数实例化你的泛型。

UsetheAsclauseinVisualBasicorthewhereclauseinC#toapplyaconstrainttoageneric.Forexample,thefollowinggenericclasscouldbeusedonlybytypesthatimplementtheIComparableinterface:

在VB中使用AS子句,在C#中使用where子句,来将约束应用到一个泛型上。例如,下面这个泛型类,只有实现了IComparable接口的类才能实例化它。

'VB

ClassCompGen(OfTAsIComparable)

Publict1AsT

Publict2AsT


PublicSubNew(ByVal_t1AsT,ByVal_t2AsT)

t1=_t1

t2=_t2

EndSub


PublicFunctionMax()AsT

Ift2.CompareTo(t1)<0Then

Returnt1

Else

Returnt2

EndIf

EndFunction

EndClass


//C#

classCompGen<T>

whereT:IComparable

{

publicTt1;

publicTt2;



publicCompGen(T_t1,T_t2)

{

t1=_t1;

t2=_t2;

}


publicTMax()

{

if(t2.CompareTo(t1)<0)

returnt1;

else

returnt2;

}

}

Theprecedingclasswillcompilecorrectly.However,ifyouremovethewhereclause,thecompilerwillreturnanerrorindicatingthatgenerictypeTdoesnotcontainadefinitionforCompareTo.ByconstrainingthegenerictoclassesthatimplementIComparable,youguaranteethattheCompareTomethodwillalwaysbeavailable.

上面描述的这个类可以正常编译。可是,假设你不使用where子句,那么编译器将返回一个错误,指出这个泛型类型T没有包含一个CompareTo的定义。通过对泛型的约束,强制它实现IComparable接口,你可以保证这个CompareTo方法始终保持有效。

Events事件

Mostprojectsarenonlinear.InWindowsFormsapplications,youmighthavetowaitforausertoclickabuttonorpressakey,andthenrespondtothatevent.Inserverapplications,youmighthavetowaitforanincomingnetworkrequest.Thesecapabilitiesareprovidedbyeventsinthe.NETFramework,asdescribedinthefollowingsections.

大部分的工程都是相互联系的。在windowforms应用程序中,你可以等待一个用户点击一个按钮或按下某个按键,然后用事件来响应它。而在服务器应用程序,你等待的是一个来自网络的请求。这些功能在.net中都是通过事件来提供。下面部分将对事件进行描述。

WhatIsanEvent?什么是事件?

Aneventisamessagesentbyanobjecttosignaltheoccurrenceofanaction.Theactioncouldbecausedbyuserinteraction,suchasamouseclick,oritcouldbetriggeredbysomeotherprogramlogic.Theobjectthatraisestheeventiscalledtheeventsender.Theobjectthatcapturestheeventandrespondstoitiscalledtheeventreceiver.

事件是对象发送的一个消息,这个消息作为信号来产生一个动作。这个动作通过与用户进行交互来引发。例如,一次鼠标点击,或者通过其他一些程序逻辑被触发。这个激活事件的对象称为事件发送者(eventsender)。捕获并响应事件的对象称为事件接受者。

Ineventcommunication,theeventsenderclassdoesnotknowwhichobjectormethodwillreceive(handle)theeventsitraises.Whatisneededisanintermediary(orpointer-likemechanism)betweenthesourceandthereceiver.The.NETFrameworkdefinesaspecialtype(Delegate)thatprovidesthefunctionalityofafunctionpointer.

在事件通信中,事件发送者无法知道哪个对象或方法将会接收事件。在事件发送者和事件接受者需要的一个中间媒介(类似指针机制)是什么呢?.net框架中定义了一个特定类型(Delegate),由它提供一个类似函数指针的功能。

WhatIsaDelegate?什么是代理(委托)

Adelegateisaclassthatcanholdareferencetoamethod.Unlikeotherclasses,adelegateclasshasasignature,anditcanholdreferencesonlytomethodsthatmatchitssignature.Adelegateisthusequivalenttoatype-safefunctionpointeroracallback.Whiledelegateshaveotheruses,thediscussionherefocusesontheevent-handlingfunctionalityofdelegates.Adelegatedeclarationissufficienttodefineadelegateclass.Thedeclarationsuppliesthesignatureofthedelegate,andthecommonlanguageruntimeprovidestheimplementation.Thefollowingexampleshowsaneventdelegatedeclaration:

代理(委托)是一个类,它控制方法中的引用。与其它类不同的是,代理(委托)类有一个签名,这个签名用来控制方法中的引用并对其进行匹配。代理(委托)相当于一个类型安全的指针或回调。代理(委托)还有其它的用处,不过在这里我们讨论的焦点是事件处理中代理的功能。

'VB

PublicDelegateSubAlarmEventHandler(senderAsObject,eAsEventArgs)


//C#

publicdelegatevoidAlarmEventHandler(objectsender,EventArgse);

Thestandardsignatureofaneventhandlerdelegatedefinesamethodthatdoesnotreturnavalue,whosefirstparameterisoftypeObjectandreferstotheinstancethatraisestheevent,andwhosesecondparameterisderivedfromtypeEventArgsandholdstheeventdata.Iftheeventdoesnotgenerateeventdata,thesecondparameterissimplyaninstanceofEventArgs.Otherwise,thesecondparameterisacustomtypederivedfromEventArgsandsuppliesanyfieldsorpropertiesneededtoholdtheeventdata.

一个具有标准签名的代理的定义是一个没有返回值的方法,这个方法只包含两个参数,第一个是引起这个事件的实例的Object类型的引用,而另一个参数是派生于EventArgs,它保存着传入事件的参数数据。如果事件本身不具有事件数据,那么这第二个参数只是一个简单的EventArgs实例。另外,这第二个参数是派生于EventArgs的自定义类型,它提供了各种字段和属性来获取事件数据。

EventHandlerisapredefineddelegatethatspecificallyrepresentsaneventhandlermethodforaneventthatdoesnotgeneratedata.Ifyoureventdoesgeneratedata,youmustsupplyyourowncustomeventdatatypeandeithercreateadelegatewherethetypeofthesecondparameterisyourcustomtype,oryoumustusethegenericEventHandlerdelegateclassandsubstituteyourcustomtypeforthegenerictypeparameter.

EventHandler是一个预定义的代理,它表现为一个没有数据的事件的事件处理方法。假设,你的事件具有事件数据,你必须提供自定义的事件数据类型,同时,你要么建立一个委托,这个委托的第二个参数是你自定义的类型,要么你必须使用泛型EventHandler代理类,并用你自定义的类型替换泛型类型参数。

Toassociatetheeventwiththemethodthatwillhandletheevent,addaninstanceofthedelegatetotheevent.Theeventhandleriscalledwhenevertheeventoccurs,unlessyouremovethedelegate.

若要将事件和事件处理方法进行关联,需要添加代理的实例给事件。除非你删除了代理,否则只要事件发生,事件处理就会被调用。

HowtoRespondtoanEvent如何设置事件处理

Youmustdotwothingstorespondtoanevent:你必须做两件事情

·Createamethodtorespondtotheevent.ThemethodmustmatchtheDelegatesignature.Typically,thismeansitmustreturnvoidandaccepttwoparameters:anObjectandanEventArgs(oraderivedclass).Thefollowingcodedemonstratesthis:

·建立一个响应事件的方法。这个方法必须和代理的签名匹配。这句话的意思是,这个方法的返回类型必须是void类型,并且具有两个参数:分别是Object和EventArgs(或者派生与EventArgs)。下面是代码示例:

·'VB

·PublicSubButton1_Click(senderAsObject,eAsEventArgs)

·'Methodcode

·EndSub

·

·//C#

·privatevoidbutton1_Click(objectsender,EventArgse)

·{

·//Methodcode

·}

·Addtheeventhandlertoindicatewhichmethodshouldreceiveevents,asthefollowingcodedemonstrates:

·添加事件处理代码用来指出哪个方法将会接收事件。

·'VB

·AddHandlerMe.Button1.Click,AddressOfMe.Button1_Click

·

·//C#

·this.button1.Click+=newSystem.EventHandler(this.button1_Click);

.NET2.0

The.NETFramework2.0includesanewgenericversionoftheEventHandlertype.

Net2.0框架包含一个新的泛型版本的EventHandler类型。

Whentheeventoccurs,themethodyouspecifiedwillrun.

然后,当事件产生时,上面这个你指定的方法将被调用并运行。

HowtoRaiseanEvent如何触发事件

Ataminimum,youmustdothreethingstoraiseanevent:在最短的时间内,你要做三件事用来触发事件。

·Createadelegate:建立一个代理

·'VB

·PublicDelegateSubMyEventHandler(ByValsenderAsObject,ByValeAsEventArgs)

·

·//C#

·publicdelegatevoidMyEventHandler(objectsender,EventArgse);

·Createaneventmember:建立一个事件成员

·'VB

·PublicEventMyEventAsMyEventHandler

·

·//C#

·publiceventMyEventHandlerMyEvent;

·Invokethedelegatewithinamethodwhenyouneedtoraisetheevent,asthefollowingcodedemonstrates:

·当你需要触发事件时,在方法中调用代理。

·'VB

·DimeAsEventArgs=NewEventArgs

·RaiseEventMyEvent(Me,e)

·

·//C#

·MyEventHandlerhandler=MyEvent;

·EventArgse=newEventArgs();

·

·if(handler!=null)

·{

·//Invokesthedelegates.

·handler(this,e);

·}

·//NotethatC#checkstodeterminewhetherhandlerisnull.

·//注意:C#中,无论handler是否为null,都会对其进行检查。

·//ThisisnotnecessaryinVisualBasic

·//而在VB中这不是必须的。

Additionally,youcanderiveacustomclassfromEventArgsifyouneedtopassinformationtotheeventhandler.

另外,假设你需要向事件处理程序传递信息,你可以自定义一个EventArgs的派生类。

DifferencesinraisingeventsinVisualBasicandC#

VB和C#在触发事件上的区别

VisualBasicandC#differwhenraisingevents.InC#,youmustcheckwhethertheeventisnullbeforecallingit.InVisualBasic,youcanomitthatcheck.

当触发事件时,VB和C#是不同的。在C#中调用事件之前,无论事件是否为null,你都必须对其进行检查确认。而在VB中,这项检查是可以忽略的。

WhatAreAttributes?什么是属性?

Attributesdescribeatype,method,orpropertyinawaythatcanbeprogrammaticallyqueriedusingatechniquecalledReflection.Somecommonusesforattributesareto

属性,某种程度上来说,它用来描述一个类型、方法或特性,可以使用一种叫反射(Reflection)的技术来查询它的目录。下面是一些属性的常用方式。

·Specifywhichsecurityprivilegesaclassrequires

·指定一个类需要的安全特性

·Specifysecurityprivilegestorefusetoreducesecurityrisk

·指定安全特性,减少安全隐患

·Declarecapabilities,suchassupportingserialization

·声明功能,例如支持序列化

·Describetheassemblybyprovidingatitle,description,andcopyrightnotice

·描述程序集标题、描述、版权信息。

AttributetypesderivefromtheSystem.Attributebaseclassandarespecifiedusing<>or[]notation.Thefollowingcodesampledemonstrateshowtoaddassemblyattributes:

属性类型派生于System.Attribute基类,通过指定<>或[]符号来使用。下面示范如何向程序集添加数据。

'VBAssemblyInfo.vb

<Assembly:AssemblyTitle("ch01vb")>

<Assembly:AssemblyDescription("Chapter1Samples")>

<Assembly:AssemblyCompany("MicrosoftLearning")>

<Assembly:AssemblyProduct("ch01vb")>

<Assembly:AssemblyCopyright("Copyright©2006")>

<Assembly:AssemblyTrademark("")>


//C#-AssemblyInfo.cs

[assembly:AssemblyTitle("ch01cs")]

[assembly:AssemblyDescription("Chapter1Samples")]

[assembly:AssemblyConfiguration("")]

[assembly:AssemblyCompany("MicrosoftLearning")]

[assembly:AssemblyProduct("ch01cs")]

[assembly:AssemblyCopyright("Copyright©2006")]

[assembly:AssemblyTrademark("")]

VisualStudioautomaticallycreatessomestandardattributesforyourassemblywhenyoucreateaproject,includingatitle,description,company,guide,andversion.Youshouldedittheseattributesforeveryprojectyoucreatebecausethedefaultsdonotincludeimportantinformationsuchasthedescription.

当你建立一个项目时,VS会为你的程序集自动建立一些标准属性,包括标题、描述、公司信息、指南和版本信息。你可以为每个建立的项目编辑这些属性,因为,项目建立时默认不包含象“描述”这样的重要信息。

Attributesdomorethandescribeanassemblytootherdevelopers,theycanalsodeclarerequirementsorcapabilities.Forexample,toenableaclasstobeserialized,youmustaddtheSerializableattribute,asthefollowingcodedemonstrates:

属性提供了更多的信息给其他开发人员,包括声明需要的条件或实现功能信息。例如,允许一个类序列化,你必须添加Serializable属性,示范代码如下:

'VB

<Serializable()>ClassShoppingCartItem

EndClass


//C#

[Serializable]

classShoppingCartItem

{

}


WithouttheSerializableattribute,aclassisnotserializable.Similarly,thefollowingcodeusesattributestodeclarethatitneedstoreadtheC:\boot.inifile.Becauseofthisattribute,theruntimewillthrowanexceptionpriortoexecutionifitlacksthespecifiedprivilege:

没有Serializable属性,类是不可以序列化的。同样的,下面代码使用属性来声明它需要读取“C:\boot.ini”文件。由于属性的定义,假设程序缺少指定权限,运行时会在执行前抛出一个异常。

'VB

ImportsSystem.Security.Permissions


<Assembly:FileIOPermissionAttribute(SecurityAction.RequestMinimum,Read:="C:\boot.ini")>

ModuleModule1

SubMain()

Console.WriteLine("Hello,World!")

EndSub

EndModule


//C#

usingSystem;

usingSystem.Security.Permissions;


[assembly:FileIOPermissionAttribute(SecurityAction.RequestMinimum,Read=@"C:\boot.ini")]

namespaceDeclarativeExample

{

classClass1

{

[STAThread]

staticvoidMain(string[]args)

{

Console.WriteLine("Hello,World!");

}

}

}

WhatIsTypeForwarding?什么是类型传递?

Typeforwardingisanattribute(implementedinTypeForwardedTo)thatallowsyoutomoveatypefromoneassembly(assemblyA)intoanotherassembly(assemblyB),andtodosoinsuchawaythatitisnotnecessarytorecompileclientsthatconsumeassemblyA.Afteracomponent(assembly)shipsandisbeingusedbyclientapplications,youcanusetypeforwardingtomoveatypefromthecomponent(assembly)intoanotherassemblyandshiptheupdatedcomponent(andanyadditionalassembliesrequired),andtheclientapplicationswillstillworkwithoutbeingrecompiled.Typeforwardingworksonlyforcomponentsreferencedbyexistingapplications.Whenyourebuildanapplication,theremustbeappropriateassemblyreferencesforanytypesusedintheapplication.

类型传递是一个属性(在TypeForwardedTo中实现),它允许你将一个类型从一个程序集(程序集A)移动到另一个程序集(程序集B),并且在客户端实例化程序集A时不需要重新编译,就可以运行。在一个组件(程序集)载入并被客户端应用程序使用后,你可以用类型传递将组件中一个类型移动到另一个程序集,而客户端应用程序仍将保持工作,不需要重新编译。类型传递只能使用在从已存在的应用程序引用的组件。当你重新编译一个应用程序时,在应用程序中使用的任何类型都必须是恰当的程序集引用(这个程序集已存在)。

Tomoveatypefromoneclasslibrarytoanother,followthesesteps:

下面这些步骤用来将一个类型从一个类库移动到另一个。

.NET2.0

Typeforwardingisanewfeaturein.NET2.0.

类型传递是net2.0框架提供的一个新的特性

1.AddaTypeForwardedToattributetothesourceclasslibraryassembly.

1.添加一个TypeForwardedTo属性到来源程序集类库。

2.Cutthetypedefinitionfromthesourceclasslibrary.

2.将类型声明代码剪切

3.Pastethetypedefinitionintothedestinationclasslibrary.

3.将剪切的类型声明代码粘贴到目的类库。

4.Rebuildbothlibraries.

4.编译两个类库

ThefollowingcodeshowstheattributedeclarationusedtomoveTypeAtotheDestLibclasslibrary:

下面代码示范将TypeA移动到DestLib类库的属性声明。

'VB

ImportsSystem.Runtime.CompilerServices

<Assembly:TypeForwardedTo(GetType(DestLib.TypeA))]>


//C#

usingSystem.Runtime.CompilerServices;

[assembly:TypeForwardedTo(typeof(DestLib.TypeA))]

Lab:CreateaDerivedClasswithDelegates练习:建立一个代理的派生类

Thefollowingexercisesdemonstrateinheritanceandevents.Ifyouencounteraproblemcompletinganexercise,thecompletedprojectsareavailableonthecompanionCDintheCodefolder.

下面的练习示范继承和事件。如果你在练习中发现了问题,你可以利用配套CD的Code目录中已经完成的项目。

Exercise1:DeriveaNewClassfromanExistingClass练习1:从一个存在的类派生一个新类

Inthisexercise,youwillderiveanewclassfromthePersonclassyoucreatedinLesson1.

在这个练习中,你将从你在第一课中建立的Penson类派生一个新类

1.CopytheChapter01\Lesson3-PersonfolderfromthecompanionCDtoyourharddisk,andopeneithertheC#versionortheVisualBasicversionoftheCreateStructproject.

1.从配套CD中拷贝Chapter01\Lesson3-Person目录到你的硬盘,打开C#

或VB版本的CreateStruct项目。

2.ChangethePersonstructuretoaclass.

2.将Person结构改为Person类

3.CreateanewclassdefinitionnamedManagerthatinheritsfromthebasePersonclass.

3.建立一个名叫Manager的新类,它继承于Person类

4.'VB

5.ClassManager

6.EndClass

7.

8.//C#

9.classManager:Person

10.{

11.}

12.Addtwonewpublicmembersasstrings:phoneNumberandofficeLocation.

12.添加两个字符串类型的公共成员:phoneNumber和officeLocation

13.Overridetheconstructortoacceptaphonenumberandofficelocationtodefinethenewmembers.Youwillneedtocallthebaseclass'sconstructor,asshowninthefollowingcodesample:

13.重写构造函数,使它可以接收电话号码和办公地址并赋给上面两个新的成员。同时你还需要调用基类的构造函数。如下代码例子:

14.'VB

15.PublicSubNew(ByVal_firstNameAsString,ByVal_lastNameAsString,_

16.ByVal_ageAsInteger,ByVal_genderAsGenders,ByVal_phoneNumberAsString,_

17.ByVal_officeLocationAsString)

18.MyBase.New(_firstName,_lastName,_age,_gender)

19.phoneNumber=_phoneNumber

20.officeLocation=_officeLocation

21.EndSub

22.

23.//C#

24.publicManager(string_firstName,string_lastName,int_age,

25.Genders_gender,string_phoneNumber,string_officeLocation)

26.:base(_firstName,_lastName,_age,_gender)

27.{

28.phoneNumber=_phoneNumber;

29.officeLocation=_officeLocation;

30.}

31.OverridetheToStringmethodtoaddthephonenumberandofficelocation,asshowninthefollowingsample:

31.重写ToString方法增加电话号码和办公地址的输出。

32.'VB

33.PublicOverloadsOverridesFunctionToString()AsString

34.ReturnMyBase.ToString+","+phoneNumber+","+officeLocation

35.EndFunction

36.

37.//C#

38.publicoverridestringToString()

39.{

40.returnbase.ToString()+","+phoneNumber+","+officeLocation;

41.}

42.ModifytheMainmethodtocreateaManagerobjectinsteadofapersonobject.Thenrunyourapplicationtoverifythatitworkscorrectly.

42.修改Main方法,建立一个Manager对象替换Person对象。然后运行你的应用程序,验证它的正确性。

Exercise2:RespondtoanEvent练习2:事件响应

Inthisexercise,youwillcreateaclassthatrespondstoatimerevent.

在这个练习中,你将建立一个类,用来响应一个timer(定时器)事件

1.UsingVisualStudio,createanewWindowsFormsapplicationproject.NametheprojectTimerEvents.

1.使用VS建立一个名叫TimerEvents的WindowForms应用程序项目

2.AddaProgressBarcontroltotheform,asshowninFigure1-2.

3.向窗体添加一个ProgressBar(进度条)控件,如图1-2



Figure1-2:Youwillcontrolthisprogressbarbyrespondingtotimerevents

图1-2通过响应的timer事件控制进度条

4.Withintheformclassdeclaration,declareaninstanceofaSystem.Windows.Forms.Timerobject.Timerobjectscanbeusedtothroweventsafteraspecifiednumberofmilliseconds.ThefollowingcodesampleshowshowtodeclareaTimerobject:

3.在窗体定义中,声明一个System.Window.Forms.Timer对象的实例。Timer(定时器)对象用于在指定的毫秒后产生事件。下面代码示范如何声明一个Timer(定时器)对象。

5.'VB

6.DimtAsTimer

7.

8.//C#

9.System.Windows.Forms.Timert;

10.Inthedesigner,viewthepropertiesfortheform.Thenviewthelistofevents.Double-clicktheLoadeventtoautomaticallycreateaneventhandlerthatwillrunthefirsttimetheformisinitialized.Withinthemethod,initializetheTimerobject,settheintervaltoonesecond,createaneventhandlerfortheTickevent,andstartthetimer.Thefollowingcodesampledemonstratesthis:

9.在设计器中,查看窗体属性。然后,查看事件列表。双击Load事件,将会自动建立一个事件处理程序,它将在窗体初始化时第一次运行。在方法中,初始化Timer对象,设置interval(时间间隔)属性为1秒,建立Tick事件处理程序,并启动timer。示例代码如下:

11.'VB

12.PrivateSubForm1_Shown(ByValsenderAsSystem.Object,ByValeAsSystem.EventArgs)_

13.HandlesMyBase.Shown

14.t=NewSystem.Windows.Forms.Timer

15.t.Interval=1000

16.AddHandlert.Tick,AddressOfMe.t_Tick

17.t.Start()

18.EndSub

19.

20.//C#

21.privatevoidTimer_Shown(objectsender,EventArgse)

22.{

23.t=newSystem.Windows.Forms.Timer();

24.t.Interval=1000;

25.t.Tick+=newEventHandler(t_Tick);

26.t.Start();

27.}

28.ImplementthemethodthatwillrespondtotheTimer.Tickevent.Whentheeventoccurs,add10totheProgressBar.Valueattribute.ThenstopthetimeriftheProgressBar.Valueattributehasreached100.Thefollowingcodesampledemonstratesthis:

27.实现Timer.Tick事件响应方法。当事件产生时,将ProgressBar.Value属性值加十。然后,判断如果ProgressBar.Value属性值到达100,则停止timer。

29.'VB

30.PrivateSubt_Tick(ByValsenderAsObject,ByValeAsEventArgs)

31.ProgressBar1.Value+=10

32.

33.IfProgressBar1.Value=100Then

34.t.Stop()

35.EndIf

36.EndSub

37.

38.//C#

39.voidt_Tick(objectsender,EventArgse)

40.{

41.progressBar.Value+=10;

42.

43.if(progressBar.Value>=100)

44.t.Stop();

45.}

46.Runtheapplicationtoverifythatitrespondstothetimereventeverysecond.

45.运行应用程序验证每秒timer产生的事件响应。

LessonSummary本课摘要

·Useinheritancetocreatenewtypesbasedonexistingones.

·通过继承,建立一个基于一个存在的类的新类。

·Useinterfacestodefineacommonsetofmembersthatmustbeimplementedbyrelatedtypes.

·使用接口来定义一个公共成员集,凡是和它有关系的类型必须实现这个公共成员集。

·Partialclassessplitaclassdefinitionacrossmultiplesourcefiles.

·飞类允许你将一个类的定义进行分割,并将各个部分放到多个源文件中。

·Eventsallowyoutorunaspecifiedmethodwhensomethingoccursinadifferentsectionofcode.

·在不同的代码部分,当有事情发生时,事件运行你指定的方法进行处理。

·Useattributestodescribeassemblies,types,andmembers.

·利用属性去描述程序集、类型和成员

·UsetheTypeForwardedToattributetomoveatypefromoneclasslibrarytoanother.

·利用TypeForwardedTo属性将一个类型从一个类库移动到另一个。

LessonReview本课复习

YoucanusethefollowingquestionstotestyourknowledgeoftheinformationinLesson3,"ConstructingClasses."ThequestionsarealsoavailableonthecompanionCDifyouprefertoreviewtheminelectronicform.

你可以使用下面的问题来测试你在本课中学到的知识。你也可以利用配套CD中的电子版。

Answers

Answerstothesequestionsandexplanationsofwhyeachanswerchoiceisrightorwrongarelocatedinthe"Answers"sectionattheendofthebook.

回答这些问题,并解释每个答案对错的原因。选项的错对可以查找在书后的“答案”部分.

1.

Whichofthefollowingstatementsaretrue?(Chooseallthatapply.)下面哪几个声明是真的?多选

A.Inheritancedefinesacontractbetweentypes.

A.继承是在类之间定义一个约束

B.Interfacesdefineacontractbetweentypes.

B.接口是在类之间定义一个约束

C.Inheritancederivesatypefromabasetype.

C.继承是从一个基类派生一个类型

D.Interfacesderiveatypefromabasetype.

D.接口是从一个基类派生一个类型



2.

Whichofthefollowingareexamplesofbuilt-ingenerictypes?(Chooseallthatapply.)下面哪几个例子是默认的泛型类型?多选

A.Nullable

B.Boolean

C.EventHandler

D.System.Drawing.Point



3.

Youarecreatingagenericclass,andyouneedtodisposeofthegenericobjects.Howcanyoudothis?

你需要释放你建立的一个泛型类的对象。你要如何做?

A.CalltheObject.Disposemethod.

A.调用Object.Dispose方法

B.ImplementtheIDisposableinterface.

B.实现Idisposable接口

C.DerivethegenericclassfromtheIDisposableclass.

C.从IDisposable类派生这个泛型类

D.UseconstraintstorequirethegenerictypetoimplementtheIDisposableinterface.

D.利用约束去要求这个泛型类型实现IDisposable接口



4.

You'veimplementedaneventdelegatefromaclass,butwhenyoutrytoattachaneventprocedureyougetacompilererrorthatthereisnooverloadthatmatchesthedelegate.Whathappened?

你要实现一个事件代理,可是当你尝试关联一个事件程序时,你得到了一个编译器错误:没有重载和匹配的代理。发生了什么?

A.Thesignatureoftheeventproceduredoesn'tmatchthatdefinedbythedelegate.

A.通过代理定义的事件程序签名不匹配

B.TheeventprocedureisdeclaredShared/static,butitshouldbeaninstancememberinstead.

B.事件程序被定义为Shared/static,并且它将被一个实例成员替换

C.Youmistypedtheeventprocedurenamewhenattachingittothedelegate.

C.当将它匹配给代理时,你用错了事件处理程序名称

D.Theclasswascreatedinadifferentlanguage.

D.类是用不同语言建立的。



Answers

1.

CorrectAnswers:BandC正确

A.Incorrect:Interfacesdefineacontractbetweentypes;inheritancederivesatypefromabasetype.

A.错误:接口定义了类型之间的约束;继承是从一个基类派生一个类型。

B.Correct:Interfacesdefineacontractbetweentypes,ensuringthataclassimplementsspecificmembers.

B.正确:接口定义类型之间的约束,确保每个类都必须实现指定的成员

C.Correct:Inheritancederivesatypefromabasetype,automaticallyimplementingallmembersofthebaseclass,whileallowingthederivedclasstoextendoroverridetheexistingfunctionality.

C.正确:继承是从一个基类派生一个类型,自动实现基类的所有成员,允许派生类扩展或重写存在的功能。

D.Incorrect:Interfacesdefineacontractbetweentypes;inheritancederivesatypefromabasetype.

D.错误:接口定义了类型之间的约束;继承是从一个基类派生一个类型。

2.

CorrectAnswers:AandC

A.Correct:Nullableisagenerictype.正确:Nullable是一个泛型类型

B.Incorrect:Booleanisanongenericvaluetype.错误:Boolean是一个非泛型的值类型。

C.Correct:EventHandlerisagenerictype.正确:EventHandler是一个泛型类型

D.Incorrect:System.Drawing.Pointisastructureandisnotgeneric.错误:System.Drawing.Point是一个结构并且不是泛型结构。

3.

CorrectAnswer:D

A.Incorrect:TheObjectclassdoesnothaveaDisposemembermethod.Additionally,youwouldneedtouseaconstrainttomandatetypesimplementingtheIDisposableinterfacetocalltheDisposemethod.

A.错误:Object类没有Dispose方法。另外,你需要使用约束要求类型实现IDisposeable接口,并调用Dispose方法。

B.Incorrect:Implementinganinterfacedoesnotenablegenerictypestouseinterfacemethods.

B.错误:实现接口并不能让泛型类型使用接口方法。

C.Incorrect:Derivingthegenericclassfromaninterfacedoesnotenablegenerictypestouseinterfacemethods.

C.错误:从一个接口派生这个泛型类,并不能让泛型类型使用接口方法

D.Correct:Ifyouuseconstraintstorequiretypestoimplementaspecificinterface,youcancallanymethodsusedintheinterface.

D.正确:如果你使用约束要求类型实现一个指定接口,你可以调用接口中的任何方法并使用。

4.

CorrectAnswer:A

A.Correct:Delegatesdefinethesignature(argumentsandreturntype)fortheentrypoint.

A.正确:代理为输入点定义签名(参数和返回类型)

B.Incorrect:EventprocedurescanbeShared/staticorinstancemembers.

B.错误:事件程序可以被Shared/static或实例化成员

C.Incorrect:Ifyoumistypedtheeventprocedurename,youwouldreceiveadifferentcompilererror.

C.错误:如果你用错了事件处理程序的名字,你得到的将是一个不同的错误。

D.Incorrect:Eventsworkequallywell,regardlessofthelanguageused.

D.错误:事件正常工作与使用的语言无关

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐