1. Trang chủ
  2. » Công Nghệ Thông Tin

C# 2005 Programmer’s Reference - chapter 14 docx

105 285 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 105
Dung lượng 12,93 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

You can look at an abstract class as across between normal classes and interfaces, in that they can have their own implementa-tion yet define an interface through abstract members that d

Trang 1

CHAPTER 14 Implementing Abstract Classes and Interfaces

This chapter shows how to create both abstract classes and interfaces You’ll see how bothcan help create interfaces for other code to use Another important point is that you’ll seehow to write code that implements interfaces

Abstract Classes

Abstract classes are a special type of base classes In addition to normal class members,there are abstract class members These abstract class members are methods and propertiesdeclared without an implementation All classes derived directly from abstract classes mustimplement these abstract methods and properties You can look at an abstract class as across between normal classes and interfaces, in that they can have their own implementa-tion yet define an interface through abstract members that derived classes must imple-ment C# interface types are covered later in this chapter, but for right now you need toknow that interfaces do not contain any implementation, a fact that differentiates themfrom abstract classes that can have implementation

Abstract classes can never be instantiated This would be illogical, because of the memberswithout implementations So what good is a class that can’t be instantiated? Lots! Abstractclasses sit toward the top of a class hierarchy They establish structure and meaning tocode They make frameworks easier to build This is possible because abstract classes haveinformation and behavior common to all derived classes in a framework Take a look atthe following example:

abstract public class Contact

public abstract void GenerateReport();

abstract public string Name

Trang 3

CHAPTER 14 Implementing Abstract Classes and Interfaces

The abstract base classContacthas two derived classes:CustomerandSiteOwner Both

of these derived classes implement the abstract members of theContactclass TheGenerateReportmethod in each derived class has anoverridemodifier in its declara-tion Likewise, theNamedeclaration contains anoverridemodifier in bothCustomerandSiteOwner

Theoverridemodifier for the overridden GenerateReportmethod and Nameproperty ismandatory C# requires explicit declaration of intent when overriding methods Thisfeature promotes safe code by avoiding the accidental overriding of base class methods,which is what actually does happen in other languages Leaving out the overridemodi-fier generates an error Similarly, adding a newmodifier also generates an error Abstractmethods are implicitly virtual They must be overridden and cannot be hidden, which thenewmodifier or the lack of a modifier would be trying to do

Notice the namefield in the Contactclass It has a protectedmodifier Remember, aprotectedmodifier allows derived classes to access base class members In this case, itenables the overridden Nameproperty to access the namefield in the Contactclass

Abstract Class and Interface Differences

Interfaces are specifications defining the type of behaviors a class must implement Theyare contracts a class uses to allow other classes to interact with it in a well-defined andanticipated manner Interfaces define an explicit definition of how classes should interact.Abstract classes are a unit of abstraction, whereas interfaces define further specification.Abstract class members may contain implementations The exception is when an abstractclass member has an abstract modifier Derived classes must implement abstract classmembers with an abstract class modifier, but they don’t have to implement any other

Trang 4

Defining Interface Types

method declared virtual On the other hand, classes inheriting an interface must ment every interface member Interface members have no implementation

imple-Implementing Interfaces

As stated in the preceding section, a C# interface defines a contract for all custom class

and struct objects that implement that interface This means that you must write code inthe class or struct that implements the interface, to provide the implementation for theinterface Instead of just exposing public members, the interface is enforced by the C#

compiler, and you must define the members specified in the interface (contract) First, Ishow you how to define an interface and then follow up with explanations of how to

implement interface members Later, you see how to use the interface

Defining Interface Types

The definition of an interface is much like a class or struct However, because an interfacedoesn’t have an implementation, its members are defined differently The following

example shows how to declare an interface and how an interface is structured:

[modifiers] interface IName [: Interface [, Interfaces]]

The modifiers can be publicorinternal, just like other type definitions Next is the

keywordinterfacefollowed by the interface name, IName, which is simply an identifier Acommon convention is to make the first character of an interface name the uppercase

letter I After the name is a colon and a comma-separated list of interfaces that this one

inherits The colon/inherited interface list is optional

Although classes have only single class inheritance, they have multiple interface tance Along the same lines, structs and interfaces have multiple interface inheritance

inheri-Later sections of this chapter explain some of the issues involved with multiple interfaceinheritance

Following the interface inheritance list is the interface body, which consists of the

members enclosed in curly braces Legal members are methods, properties, indexers, andevents Interface members are assumed to be public and, therefore, have no modifiers

Interface implementations must also be public

Trang 5

CHAPTER 14 Implementing Abstract Classes and Interfaces

The following sections explain how to define interface members for the following face,IBroker:

inter-public interface IBroker

Methods

Interface methods are defined similarly to normal methods The difference is that theyhave no implementation The following example shows an interface method:

string GetRating(string stock, out string provider) ;

Everything is the same as a normal method, except that a semicolon is used at the end inplace of the implementation code block

Properties

At first, it might seem strange that a property could be an interface member, especiallywhen the normal implementation of a property is associated with a field Although fieldscan’t be interface members, this doesn’t prevent the use of properties, because the imple-mentation of a property is independent of its specification

Remember, one of the primary reasons for properties is to encapsulate implementation.Therefore, the fact that an interface doesn’t have fields is not a limiting factor For thisreason, property specifications may be added to interfaces with no problem, as shown inthe following example:

Trang 6

This example shows an indexer accepting a string argument and returning a decimal

value Its getandsetaccessors are closed with semicolons, similar to how property sors are defined

event ChangeRegistrar PriceChange ;

As you can see, the event has the exact same type of signature that goes in a normal class

No surprises

Implicit Implementation

It is easy to implement a single interface on a class or struct It just requires declaration ofthe class or struct with interface inheritance and the implementation of those interfacemembers There are two views of implicit interface implementation The first is the easiest:

a single class implementing a single interface The second uses interface polymorphism byimplementing the same interface in two separate classes

Single Class Interface Implementation

As mentioned previously, it is easy for a class to implement a single interface ation of an interface simply follows the rules set in previous sections The interface in

Implement-Listing 14.1 represents a plausible set of class members that a stockbroker or financial

company may want to expose to a client

LISTING 14.1 TheIBroker Interface Definition

using System;

using System.Collections;

Trang 7

CHAPTER 14 Implementing Abstract Classes and Interfaces

up to a class or struct to implement interface member declarations Listing 14.2 shows aclass that implements the IBrokerinterface, which is an object that represents a financecompany It has an overridden constructor to ensure that the pricePerTradefield isinitialized properly

LISTING 14.2 An Implementation of the IBroker Interface

public class FinanceCompany : IBroker

{

Dictionary<string, decimal> m_stocks

= new Dictionary<string, decimal>();

Trang 8

ThePriceChangeevent is based on the ChangeRegistrardelegate, which specifies two

object parameters When the PriceChangeevent is invoked in the setaccessor of the

Trang 9

CHAPTER 14 Implementing Abstract Classes and Interfaces

PricePerTradeproperty, it receives two arguments The string argument is implicitlyconverted to object The decimal value is boxed and passed as an object Event declarationand implementation are normally as simple as shown in Listing 14.2 However, the eventimplementation can be much more sophisticated if there is a need to override its addandremoveaccessors

TheGetRatingmethod is implemented to always return the same value In this context,the broker is always bullish, regardless of the real value of a stock

The indexer implementation uses the Dictionary<string, decimal>collection for taining its data This is a generic collection where the key is type string and the value istype decimal You’ll learn more about generics in Chapter 17, “Parameterizing Type withGenerics and Writing Iterators.” The indexer’s getaccessor returns the value of a stockusingStocknameas a key The setaccessor creates a new Hashtableentry by using theindexer string parameter as a key and the value passed in as the hash value

main-Now there’s a class that faithfully follows the contract of the IBrokerinterface What’sgood about this is that any program can now use that class and automatically know that ithas specific class members that can be used in a specific way Listing 14.3 shows a

program that uses a class that implements the IBrokerinterface

LISTING 14.3 Implementation of Single Interface Inheritance

public class InterfaceTester

{

public static int Main()

{

FinanceCompany finco = new FinanceCompany();

InterfaceTester iftst = new InterfaceTester();

finco.PriceChange += new ChangeRegistrar(

iftst.PricePerTradeChange);

finco[“ABC”] = 15.39m;

finco[“DEF”] = 37.51m;

Console.WriteLine(“ABC Price is {0}”, finco[“ABC”]);

Console.WriteLine(“DEF Price is {0}”, finco[“DEF”]);

Trang 10

“Trading price for {0} changed to {1}.”,

(string) sender, (decimal) evnt);

Trading price for FinanceBroker changed to 10.55

finco’s recommendation for ABC is Buy

Because the FinanceCompanyclass implements the IBrokerinterface, the program in

Listing 14.3 knows what class members it can implement The Mainmethod instantiates aFinanceCompanyclass,finco, and an InterfaceTesterclass,iftst

TheInterfaceTesterclass has an event handler method named PricePerTradeChange

In the Mainmethod, the InterfaceTesterclass makes itself a subscriber to the

finco.PriceChangeevent by assigning the PricePerTradeChangeevent handler to that

event

Next, two stocks are added tofinco This is done by using a stock name as the

indexer and giving it a decimal value The assignment is verified with a couple

Console.WriteLinemethods

Thefincoobject’s PricePerTradeproperty is changed to 10.55m Within the

FinanceCompanyclass, this invokes the PriceChangeevent, which calls the PricePerTrademethod of the InterfaceTesterclass This shows how events are effective tools for

obtaining status changes in an object Finally, the GetRatingmethod of the fincoobject

is invoked Method calls are the most typical interface members

The output follows the sequence of events in Main The first two lines show the stock

values from the indexer The third line is from the PricePerTradeChangeevent handler intheInterfaceTesterclass The last line of output shows the results of requesting a stockrating from the fincoobject

Trang 11

CHAPTER 14 Implementing Abstract Classes and Interfaces

Simulating Polymorphic Behavior

Implementing an interface in a single class and using it is relatively easy, as described inthe preceding section However, the real power of interfaces comes from being able to usethem in multiple classes Let’s take a look at using interfaces to implement polymorphism

in a program

We will combine the examples from the previous section in a test program Listing 14.4shows another implementation of the IBrokerinterface It’s a bit more complicated thantheFinanceCompanyclass implementation of IBrokerbecause of additional objects andextra class members with more logic

LISTING 14.4 Another Implementation of the IBroker Interface

public enum StockRating

private string name;

private decimal price;

private StockRating rating;

public string Name

Trang 13

CHAPTER 14 Implementing Abstract Classes and Interfaces

LISTING 14.4 Continued

private StockRating AssignRating(Stock newStock)

{

Random myRand = new Random();

int nextRating = myRand.Next(4);

return (StockRating)Enum.ToObject(

typeof(StockRating),nextRating);

Trang 14

Implicit Implementation

Two object types are participating in the implementation of the StockBrokerclass:

StockRatingandStock The StockRatingenum is used by the StockBrokerclass to defineits rating system for individual stocks The Stockstruct defines a stock It has three privatefields, encapsulated by three corresponding properties The namefield is a string that holdsthe name of the stock A stock’s value is held in the decimal pricefield The ratingfieldholds a company’s assessment of the value of a particular stock Its type is the StockRatingenum The StockBrokerclass uses these two objects

TheStockBrokerclass implements the IBrokerinterface It has three fields that supportthis implementation The m_stocksfield is a Dictionarythat holds objects of type Stockstruct A StockBrokerobject manages the amount it charges for trades through the

decimalpricePerTradefield Its name is saved in the brokerNamestring field To supportits fields, the StockBrokerclass implements three overloaded constructors

Stockobjects are created in theStockBrokerindexer Thesetaccessor creates a new

Stockobject The name is assigned through theStockobjectNameproperty from the

StockNameindexer string parameter The price is set with the indexer value by using thePriceproperty of theStockobject When creating a rating, theAssignRating()method

is used to obtain aStockRatingenum value and assign that to theRatingproperty of theStockobject Via theDictionary.Addmethod, the stock is then added to them_stockscollection

The private AssignRatingmethod determines what type of rating each stock has by usingtheRandomclass After instantiating the myRandobject of type Random, it calls the Next

method and assigns the result to the nextRatingfield The Nextmethod of the myRand

object accepts an int parameter, indicating upper bound of the result This produces an int

in the range of 0 to 4 This is also the corresponding range of values in the StockRatingenum This value is then translated into a valid StockRatingenum value using the

ToObjectmethod of the Enumclass

To return a string type, the GetRatingmethod must first obtain the correct Stockobjectfrom the stockscollection It does this by using the string parameter stockas an indexfor the m_stockscollection After it has the stock, it uses the GetNamemethod of the Enumclass to translate the Ratingproperty, which returns a StockRatingtype, from the Stockobject into a string

ThePricePerTradeproperty is the same as the one for the FinanceCompanyclass Its getaccessor simply returns the pricePerTradefield When setting the property, the setacces-sor assigns the new value to the pricePerTradefield and then invokes the PriceChangeevent Any subscribed classes are notified with the value of the brokerNamefield and thenewpricePerTradefield value

Despite the increased complexity of the StockBrokerclass implementation over the

FinanceCompanyclass, they are both used in the same way They provide the same type ofservices because they implement the IBrokerinterface Listing 14.5 shows a program thatuses both of these classes, implementing polymorphic behavior to exploit the power ofinterfaces

Trang 15

CHAPTER 14 Implementing Abstract Classes and Interfaces

LISTING 14.5 Using Two Classes with the Same Interface

public class InterfaceTester

broker[“DEF”] = 37.51m;

Console.WriteLine(““);

Console.WriteLine(“ABC Price is {0}”, broker[“ABC”]);Console.WriteLine(“DEF Price is {0}”, broker[“DEF”]);Console.WriteLine(““);

Trang 16

Implicit Implementation

LISTING 14.5 Continued

Console.WriteLine(

“Trading price for {0} changed to {1}.”,

(string) sender, (decimal) evnt);

Trading price for FinanceBroker changed to 10.55

Broker’s recommendation for ABC is Buy

ABC Price is 15.39

DEF Price is 37.51

Trading price for Gofer Broke changed to 10.55

Broker’s recommendation for ABC is Accumulate

Listing 14.5 shows how to implement polymorphism with interfaces It does this by ing an instance of both the FinanceCompanyandStockBrokerclasses and using each

creat-through the IBrokerinterface The Mainmethod of the IntefaceTesterclass begins bydeclaring a List<IBroker>collection named brokers Because both FinanceCompanyandStockBrokerareIBrokerclasses, they can be assigned to brokers Each of the IBroker-derived classes is created and added to the brokerscollection

The primary implementation of the Mainmethod is enclosed in a foreachloop Althoughboth the FinanceCompanyandStockBrokerobjects were instantiated and placed into thebrokerscollection individually, they’re extracted as IBrokerobjects in the foreachloop.Within the foreachloop, only the IBrokerinterface members are used on each object

First, the PricePerTradeChangeevent handler is added to the PriceChangeevent of eachbroker Any time the PricePerTradeproperty of a broker changes, this event handler iscalled It’s interesting to note that this demonstrates how a single event handler can beused as a callback for multiple events Each of these multiple events is a price change foreach of the brokers

Each broker object’s stock list is initialized with the same values, and then these values areprinted, which shows that the interface works the same for all IBrokerobjects, regardless

of the IBroker-derived object’s underlying implementation

Then, the PricePerTradeproperty of each broker is updated This triggers each broker’sPriceChangeevent and invokes the PricePerTradeChangeevent handler of the

InterfaceTesterclass After that, the GetRatingmethod is called This is more

Trang 17

CHAPTER 14 Implementing Abstract Classes and Interfaces

demonstration of the power of interfaces Interface polymorphism works for all IBrokerobject members

Because the first four lines of output are from the FinanceCompanyclass, they are the same

as from the previous section Then fifth and sixth lines show the stock prices The seventhline is from the PriceChangeevent invocation, where it called the PricePerTradeChangeevent handler It prints out the name of the StockBrokercompany and the new tradingprice The last line shows the recommendation from the StockBrokerclass The recom-mendation is regenerated every time the program is run and, therefore, will most likelychange between program executions

Explicit Implementation

Sometimes it’s necessary to explicitly declare which interface a class or struct memberimplements One common reason for explicit implementation is when there is multipleinterface inheritance and two or more interfaces declare a member with the same name.Another reason to use explicit interface implementation is to hide a specific implementa-tion

To perform explicit interface implementation, a class implements an interface member byusing its fully qualified name The implementation is not declared with modifiers, becausethey are implicitly hidden from an object of the implementing class However, they areimplicitly visible to objects of the explicit interface type The examples in this chaptershow how this occurs

Disambiguation of interfaces occurs when a class inherits two or more interfaces that havemembers with the same signature Normally, a class can just implement the interfacemember, regardless of which interface it is a part of However, sometimes it might benecessary to specify the interface in a user class For this reason, explicit implementation

is necessary to specify which implementation serves which interface Listing 14.6 showsthe implementation of two interfaces that have some members in common

LISTING 14.6 A Couple of Interfaces with Identical Members

Trang 18

members in the IBrokerinterface.

Sometimes it might be necessary to hide the implementation of an interface so that theparticular member is private to the implementing class and users won’t know about it

Hiding interface implementations can occur regardless of whether there are one or moreinherited interfaces

Hiding interface members with explicit implementation is not like hiding an inherited

method The difference is that a conversion is required to reference the explicit interfacemember definition It generally indicates that the interface is not of particular interest to auser of that class or struct Listing 14.7 demonstrates explicit interface implementation

Trang 19

CHAPTER 14 Implementing Abstract Classes and Interfaces

LISTING 14.7 Explicit Interface Implementation

public class FinancialAdvisor : IBroker, IAdvisor

Random myRand = new Random();

int nextRating = myRand.Next(4);

return (StockRating)Enum.ToObject(

typeof(StockRating),nextRating);

}

Trang 21

CHAPTER 14 Implementing Abstract Classes and Interfaces

Listing 14.7 shows how to use explicit interface implementation to disambiguate theimplementation of interface methods and the hiding of interface members The code issimilar to the StockBrokerclass implementation with a few exceptions

TheFinancialAdvisorclass inherits both the IBrokerandIAdvisorinterfaces Commasseparate multiple interface inheritance in class and struct declarations This class has threeexplicit interface member implementations

TheGetRatingmethod has two explicit member implementations The first is the explicitimplementation of the IBroker.GetRatingmethod It obtains the rating from the Stockobject returned from the m_stockscollection

The second explicit implementation is the IAdvisor.GetRatingmethod It’s similar to theIBroker.GetRatingmethod implementation, except that the myStock.Ratingobject ismanipulated before being converted to a string This manipulation consists of increment-ing its value, casting it to an int type, and performing a modulus operation with the value

5to keep it in the range of legal StockRatingvalues

The third explicit implementation is the IAdvisor.HourlyFeesproperty This property isessentially hidden to a using object of type FinancialAdvisor This is how interfacemembers are hidden

The first two properties are also hidden to objects of type FinancialAdvisorclass

However, they serve to disambiguate the implementation of that member to using classes.Listing 14.8 shows how these two members are used

LISTING 14.8 Implementation of Explicit Interface Members

public class InterfaceTester

{

public static int Main(string[] args)

{

string recommendation;

FinancialAdvisor finad = new FinancialAdvisor();

InterfaceTester iftst = new InterfaceTester();

finad.PriceChange += new ChangeRegistrar(

iftst.PricePerTradeChange);

finad[“ABC”] = 15.39m;

finad[“DEF”] = 37.51m;

Console.WriteLine(“ABC Price is {0}”, finad[“ABC”]);

Console.WriteLine(“DEF Price is {0}”, finad[“DEF”]);

Console.WriteLine(““);

finad.PricePerTrade = 10.55m;

Trang 22

“Trading price for {0} changed to {1}.”,

(string) sender, (decimal) evnt);

}

}

Listing 14.8 shows how to use class members that were explicitly implemented to avoidambiguity It also has a commented member that shows how an error would be generatedfor an explicit implementation of an interface member for the purpose of hiding The

Mainmethod of the InterfaceTesterclass contains code that tests the FinancialAdvisorclass implementation

TheFinancialAdvisorclass is instantiated, the PricePerTradeChangeevent handler is

added to the PriceChangeevent, and the stock values are instantiated as in examples inprevious sections The first difference in this code is the commented section where there is

an instruction to load the HourlyFeesproperty of the finadobject with a value If this

were uncommented, it would produce a compiler error because the code wouldn’t nize the HourlyFeesproperty of the FinancialAdvisorclass because that property is

recog-hidden through explicit interface implementation

The next portion of code uses the GetRatingmethods of the FinancialAdvisorclass Thedifference between the two calls is the object type used Each is cast to a separate interface

Trang 23

CHAPTER 14 Implementing Abstract Classes and Interfaces

type The object with the IBrokercast invokes the explicit implementation of

IBroker.GetRating Similarly, the IAdvisorcast causes invocation of the

IAdvisor.GetRatingexplicit member implementation This is how explicit

implementation for disambiguation of interface implementation is used

SAFER CONVERSIONS

The example in Listing 14.8 made the assumption that the classes implemented the

interfaces In a production environment, it might be more appropriate to use the isand

asoperators to avoid the exception that could be raised

Interface Mapping

Interface mapping is the method used to determine where and if an interface member isimplemented Interface mapping is important because programmers need a way to figureout why they’re getting program errors for not implementing an interface Anotherscenario might be strange program behavior because of an interface member implementedsomewhere other than the class that directly inherits from the interface The solution is tounderstand enough about interface mapping to determine whether and where an interfacemember was implemented

In all previous cases, mapping was easy to determine: It happened directly in the derivedclass that implemented that interface Most interface implementation occurs that way.However, interface mapping allows alternative ways to determine whether an interface hasbeen implemented Besides the directly derived class of the interface, an implementationcould be in the parent class hierarchy of the class derived from the interface This followsobject-oriented principles where a derived class “is” an inherited class Remember, whendeclaring inheritance relationships with both a class and interfaces, the class comes first inthe list Using the IBrokerandIAdvisorinterfaces from previous sections in this chapter,Listing 14.9 shows an example of how this could occur

LISTING 14.9 Interface Mapping Example

public class StockBroker

Trang 24

Random myRand = new Random();

int nextRating = myRand.Next(4);

return (StockRating) Enum.ToObject(

typeof(StockRating),nextRating);

Trang 25

CHAPTER 14 Implementing Abstract Classes and Interfaces

public class Accountant : StockBroker, IBroker

If the StockBrokerclass did not implement a given interface member, the Accountantclass would then be required to implement that member Remember, every member of aninterface must be implemented

Interface Inheritance

Interfaces can inherit from each other This makes it possible to create an abstract chy of interfaces that support some domain When interfaces inherit from each other, theyinherit the contract of the interfaces above them Also, interfaces can inherit multiply fromother interfaces The following example shows how one interface can inherit from another:

Trang 26

string GetRating(string stock) ;

decimal this[string StockName]

TheIBrokerinterface inherits thePricePerTradeproperty declaration and the

PriceChangeevent declaration from theIShareTradeinterface All combined, these arethe same interface members from theIBrokerinterface of previous sections Therefore,any class inheriting theIBrokerinterface will have the exact same set of interface

members to implement, which is also the exact same contract Listing 14.10

demon-strates that the contract of inherited interfaces is passed with derived interfaces to

derived classes and structs for implementation

LISTING 14.10 Interfaces Inheriting Other Interfaces

public class FinanceCompany : IBroker

{

Dictionary<string, decimal> m_stocks

= new Dictionary<string, decimal>();

Trang 27

CHAPTER 14 Implementing Abstract Classes and Interfaces

Trang 28

OBJECT VERSUS INTERFACE INHERITANCE

Interface inheritance is not the same as object inheritance in the areas of

implementa-tion and multiplicity Object inheritance means that all derived objects also inherit

implementation of base objects Because interfaces don’t have implementations,

derived classes have the responsibility to implement interface members

Object inheritance is limited in that there is only single implementation inheritance In

other words, derived classes can inherit from only one class However, classes and

structs can inherit as many interfaces as you need

Summary

You now know how to implement an abstract class You’ve seen how abstract classes

have normal implementation class members and abstract members

You also learned about interfaces and their members I continued by showing how bothimplicitly and explicitly implement interfaces You also saw how interface mapping worksand how to inherit other interfaces

This finishes the object-oriented programming part of this book Now it’s time to turn tosome more advanced features In the next section, you learn how to properly manage

object lifetime, implement reflection, use lambdas, and learn how to use generic types (afterI’ve been teasing you with generic code snippets for so long already)

Trang 29

This page intentionally left blank

Trang 30

PART 3

Applying Advanced C# Language Features

CHAPTER 15 Managing Object Lifetime

CHAPTER 16 Declaring Attributes and Examining Code

with Reflection CHAPTER 17 Parameterizing Type with Generics and

Writing Iterators CHAPTER 18 Using Lambdas and Expression Trees

Trang 31

This page intentionally left blank

Trang 32

CHAPTER 15 Managing Object

Lifetime

IN THIS CHAPTER

Object Initialization Object Initializers Object Finalization Automatic Memory Management Proper Resource Cleanup Interacting with the GarbageCollector

We enter this world with great fanfare, live our lives, and

eventually get old and die Some people have more drama

in their lives than others, but that’s often a consequence of

the environment they are in and how they’re raised One

thing is for sure: The beginning and end of life are

signifi-cant events

Similar to people, the objects of our applications have a

life-time, which includes a beginning, middle, and end Objects

begin life via instantiation In C#, that means a constructor

invocation Just like the level of drama in a person’s life, the

attributes and behavior of an object interacting with user

input determines what happens to that object and its affect

on other objects in the system Predicting when a person

will leave this world is as accurate as when the Common

Language Runtime (CLR) will clean an object from

memory—you don’t know Nevertheless, there is a

signifi-cant process the CLR engages in to ensure the end of that

object’s lifetime is handled properly

Throughout this book, we discuss what happens to an

object during its lifetime However, the purpose of this

chapter is to drill down on what happens when an object is

first created and when that object is no longer being used

To do this, you learn about constructors to ensure an object

is instantiated properly We also devote significant attention

to the CLR memory management process, which is essential

to understanding how an object is handled at the end of its

lifetime

Let’s start at the beginning, learning about constructors

Trang 33

CHAPTER 15 Managing Object Lifetime

Object Initialization

You’ve been implicitly using constructors in all the C# programs written so far Everyobject in C# has a constructor, whether default or defined When you don’t define explicitconstructors for an object, C# creates a default constructor behind the scenes This sectionshows you how constructors work and covers some tricky scenarios that could catch youoff guard if you don’t have a full understanding of how constructors work Much of thematerial focuses on instance constructors, but I also show you how to define static

constructors C# 3.0 introduced a new way to initialize objects, called type initializers.After you learn how constructors work, I show you how type initializers can make objectconstruction even easier

Instance Constructors

The purpose of instance constructors is to initialize object instance fields Instance

constructors are convenient because they provide a centralized place to instantiate fields.Instance constructors also enable the ability to dynamically alter the initial values offields, based on arguments passed to the constructor when the instance of a class iscreated Constructors are invoked only on instantiation and can’t be called by otherprograms later

WHERE ARE CONSTRUCTORS DECLARED?

You can define constructors for both class and struct custom types This chapter uses

the term object, but you can’t declare constructors for custom delegate, enum, or

interface types, which wouldn’t make sense because of the way they are used

Declare constructors with the same name as the class of which they are members Youmay include zero or more parameters for users to pass initialization information

Constructors don’t have return values The following example declares a constructor fortheWebSiteclass, which accepts three parameters to initialize the class with:

public class WebSite

siteName = newSite;

Trang 34

Notice that the constructor name is exactly the same as the class name, which is required.

It has three parameters that have values that accept caller arguments, passed whenever theclass is instantiated Constructor parameters are just like method parameters

Constructors do not have a return value Their purpose is to instantiate an object and thatobject is returned when instantiating via the newoperator The following statement usesthenewoperator to create an instance of the WebSiteclass, effectively calling the previousWebSiteconstructor:

WebSite mySite = new WebSite(

“Computer Security Mega-Site”,

“http://www.comp_sec-mega_site.com”,

“Computer Security Information”);

This example instantiates a new object of class type WebSite The three arguments are

passed to the WebSiteconstructor so that this WebSiteinstance can be initialized with therequested data

Sometimes it’s a pain in other languages when trying to figure out new names for the

same thing It often makes sense to use a meaningful name and then have a conventionspecifying which object is being referred to That’s what the thiskeyword can be used for,

as shown in the following example:

public class WebSite

Trang 35

CHAPTER 15 Managing Object Lifetime

this.siteNamerefers to the class field siteName Within the constructor, siteName

(without the thiskey word) refers to the parameter siteName

When the constructor in the example executes, it instantiates fields with the values of the parameters This is normal—objects are customized by specifying unique argumentsduring their instantiation

Overloading Constructors

Objects are not limited to a single constructor because you are allowed to overloadconstructors A constructor overload works the same as a method overload where eachoverload differs by number and type of parameter The goal of constructor overloadsshould be to make it easier and more intuitive for the caller to instantiate your object.Here’s an example that overloads the WebSiteclass constructors:

public class WebSite

: this(“No Site”, “no.url”, “No Description”) {}

public WebSite(string newSite)

: this(newSite, “no.url”, “No Description”) {}

public WebSite(string newSite, string newURL)

: this(newSite, newURL, “No Description”) {}

public WebSite(string newSite,

string newURL,string newDesc){

This example shows multiple constructors They’re primarily differentiated by the number

of parameters they accept The last constructor with the three parameters is familiarbecause it’s identical to a previous example The most notable difference between all the

Trang 36

Object Initialization

other constructors and the one with three parameters is that the other constructors have

no implementation

The first three constructors also have a different declaration After the constructor name

is a colon and the keyword this The thiskeyword refers to the current instance of anobject When used with a constructor declaration, the thiskeyword calls another

constructor of the current instance

The first three constructors have zero, one, and two parameters in their parameter list Theeffect is that the last constructor with three parameters is called Because none of the otherconstructors have three parameters of their own, they supply all the information they

have available and then add a default value to the argument they don’t have a value for.Each of the first three constructors could have its own implementations However, this isrisky, and any time it becomes necessary to modify an object’s initialization, all the

constructors would have to be modified This could lead to bugs, not to mention sary work By using the thiskeyword in every constructor to call a single constructor thatimplements the object initialization, a class becomes more robust and easier to maintain

unneces-In class initialization, there is a defined order of initialization, as follows:

1 Class field initializers This guarantees they can be initialized before one of the

constructors tries to access them

2 Other constructors called with the thisoperator

3 Statements within the constructor’s block

Multiple constructors provide a flexible way to instantiate a class, depending on an cation’s needs It can also contribute to making a class more reusable

appli-Default Constructors

If desired, an object can be declared with no constructors at all—this does not mean thatthe object doesn’t have a constructor, because C# implicitly defines a default constructor.This allows an object to be instantiated regardless of whether it has a constructor Defaultconstructors have no parameters

If a class declares one or more constructors, a default constructor is not created cally Therefore, it’s usually a good idea to include a parameterless constructor, just in casesomeone tries to instantiate a class with no parameters A parameterless constructor canalso come in handy with automated tools

automati-Private Constructors

Normally, constructors are declared with public access, but sometimes it’s necessary to

declare a private constructor A single private constructor can prevent the class from beingderived from or instantiated This is useful if all of a class’s members are static because itwould be illogical to try to instantiate an object of this class type

Trang 37

CHAPTER 15 Managing Object Lifetime

Here’s an example of a class where the constructor is private and the methods are static:public class MortgageCalculations

Inheritance and Order of Instantiation

When instantiating classes in an inheritance chain, you need a deterministic way to knowwhich classes instantiate first This section clarifies what you need to know about theorder of invocation for constructors and some tricky scenarios you’ll want to know about.This section concentrates on instance fields, but what about static fields? Static fieldsshouldn’t be accessed within instance constructors because that could possibly createredundant code to execute every time a new instance is created or leave a class in aninconsistent state when subsequent static methods, if any, are invoked A good rule of

Trang 38

initial-execution, static constructors are invoked only one time each time your program runs.

Static constructors can access only static fields There are two reasons for this First, staticclasses are loaded before any instance of an object can be created It stands to reason,

therefore, that instance fields may not have been initialized at that point in time

Certainly, the instance constructor, which is invoked when an object is instantiated,

hasn’t been invoked yet It may never be invoked Therefore, there shouldn’t be any

attempts to access uninstantiated fields Such behavior would violate definite assignmentand cause funny things to happen to the code (or not so funny, depending on one’s

perspective)

Second, the purpose of instance fields is just as their name implies, for a specific instance.Static fields are applicable to the type, regardless of instance Therefore, trying to accessinstance fields in a static constructor would be illogical Here’s an example of a static

constructor, which initializes a static field:

public class Randomizer

Because it’s never instantiated, parameters wouldn’t make sense

Static constructors cannot be called by programs They’re invoked only when a class is

loaded and there is no specified sequence of operations for when a static constructor is

invoked, but there are a few conditions that can be relied on:

The type is loaded before the first instance is created

The type is loaded prior to accessing static members

Trang 39

CHAPTER 15 Managing Object Lifetime

The type is loaded ahead of any derived types

The type is loaded only one time per program execution

public StaffMember Doctor { get; set; }

public StaffMember LeadNurse { get; set; }

public StaffMember AssistantNurse { get; set; }

public StaffMember Intern1 { get; set; }

public StaffMember Intern2 { get; set; }

public StaffMember Intern3 { get; set; }

public StaffMember Intern4 { get; set; }

}

With type initializers, you don’t need to worry about constructors Here’s how you caninitialize a StaffMemberobject:

var staffMemberNew = new StaffMember { Name = “Joe” };

Between the curly braces, identify the field or property, use the assignment operator, andthen specify the value You can also initialize multiple properties by separating them withcommas Here’s a more elaborate example that initializes a MedicalOfficewith a DoctorandLeadNurse:

var medOff = new MedicalOffice

Trang 40

Object Finalization

Name = “Nancy”

}};

In this case, the values assigned to both DoctorandLeadNurseare declared with a typeinitializer, too

Object Finalization

The original purpose of finalizers was to clean up resources when an object is cleaned up

by the Garbage Collector (GC) However, because of the way the CLR GC handles objects,there is the distinct possibility of a finalizer not being called Furthermore, there is no

precise time after which your object is destroyed that a finalizer is called, which causes

problems in scalability This section shows you how finalizers work because they are classmembers, but you should read the next section to understand the problems with finalizersand the proper way to clean up object resources

The following example shows how to define a finalizer It adds a finalizer to the WebSiteclass:

public class WebSite

The preceding example shows a typical implementation All finalizers begin with the tilde,

~, symbol Their names are the same as their enclosing class name Their parameter listsare empty They do not return a value—finalizers can’t be called by functions, so returnvalues wouldn’t make sense

Using a finalizer to clean up class resources increases the risk of resource depletion, systemcorruption, or deadlock The basic rule is this: Don’t depend on a finalizer to release criti-cal resources being held by a class The next section begins a journey to understanding

Ngày đăng: 12/08/2014, 09:23

TỪ KHÓA LIÊN QUAN