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 1CHAPTER 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 3CHAPTER 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 4Defining 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 5CHAPTER 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 6This 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 7CHAPTER 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 8ThePriceChangeevent is based on the ChangeRegistrardelegate, which specifies two
object parameters When the PriceChangeevent is invoked in the setaccessor of the
Trang 9CHAPTER 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 11CHAPTER 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 13CHAPTER 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 14Implicit 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 15CHAPTER 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 16Implicit 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 17CHAPTER 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 18members 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 19CHAPTER 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 21CHAPTER 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 23CHAPTER 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 24Random myRand = new Random();
int nextRating = myRand.Next(4);
return (StockRating) Enum.ToObject(
typeof(StockRating),nextRating);
Trang 25CHAPTER 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 26string 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 27CHAPTER 14 Implementing Abstract Classes and Interfaces
Trang 28OBJECT 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 29This page intentionally left blank
Trang 30PART 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 31This page intentionally left blank
Trang 32CHAPTER 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 33CHAPTER 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 34Notice 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 35CHAPTER 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 36Object 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 37CHAPTER 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 38initial-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 39CHAPTER 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 40Object 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