Library tutorials & articles
A Twisted Look at Object Oriented Programming in C#
Static Methods, Factories & Constructors
Well, I've tried as long as possible to avoid the the "nuts and bolts" of object oriented programming. It's sort of like going in to the dentist for a root canal. You know it needs to be done, but it is going to be painful and you want to put it off. The good news is that once you have the root canal the pain goes away! So just dive in. In this chapter you will learn about static methods, factory methods and constructors. You will be introduced to the creational patterns "Class Factory" and "Singleton".
What Is a Static Field or Method?
Let's change the question. When is a field or method not part of an object? Answer: when it is part of the class! Remember, an object is an instance of a class and each object exists in a separate space in memory. It is possible to access class fields and class methods without creating an instance of a class using the "static" key word. Declaring a field or method with the static key word, tells the compiler that the field or method is associated with the class itself, not with instances of the class. In a sense, static or "class" fields and methods are global variables and methods that you can touch using the class name. If you think of a class as a blueprint used to create objects, then you can think of static fields and methods are being part of the blueprint itself. There is only one copy of the static fields and methods in memory, shared by all instances of the class.
Static fields are useful when you want to store state related to all instances of a class. A counter is a good example of a static field. The classic use of a static counter is to generate a unique ID or serial number for each instance of a class.
Static methods are useful when you have behavior that is global to the class and not specific to an instance of a class. In contrast, instance methods are useful when the method needs to know about the state of an object. Since data and behavior are intertwined in an object, instance methods have access to the instance fields and can exhibit behavior that is specific to the state of an object.
A Static Counter
Here is the classic example of a static counter that is zero based. It contains a static field "uniqueID" and a thread safe static method "GetUniqueID" that increments the unique ID:
/// <summary>
/// Summary description for TestStatic.
/// </summary>
class TestStatic
{
// static stuff
private static int uniqueID= 0;
private static int GetUniqueID()
{
lock(typeof(TestStatic))
{
return uniqueID++; // returns zero
at start
}
}
// member stuff
private int identity;
public TestStatic()
{
this.identity= TestStatic.GetUniqueID();
}
public int Identity
{
get
{
return identity;
}
}
}
public class Test
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
//
// TODO: Add code to start application here
//
TestStatic ts1= new TestStatic();
TestStatic ts2= new TestStatic();
Console.WriteLine(ts1.Identity.ToString());
Console.WriteLine(ts2.Identity.ToString());
Console.ReadLine();
}
}
If you compile and run this code the output is: 0 and 1. The static field "uniqueID" is global to the application and stores the value of the next unique ID. Each call to the constructor returns the unique ID and then increments the counter using the "postfix" operator ++. Notice how you use the class name to touch a static field or method:
ClassName.fieldName;
ClassName.MethodName();
Note: In Java you can touch a static field or method using the class name or a reference variable to an instance of a class. Not so in C#.
Managing Concurrency Conflicts
The curious coder will note the call to "lock" which causes callers of the static method "GetUniqueID" to queue up to this method. (Lock is basically a shortcut to "Monitor.Enter" and "Monitor.Exit".) Locking inside the method insures that the method is thread safe. The problem is that the increment operator (++) is not an atomic operation, but performs a read and then a write. If you don't force callers to queue up to the increment operation it is possible for two callers to "almost simultaneously" enter the method. Both callers could read the uniqueID value before either caller can write the incremented value. In this case, both callers will receive the same ID. Not very unique! Be careful. If your locking code is poorly written, it is possible for two callers to queue up in a "catch-22" conflict where neither call can proceed, an example of "deadlock." The topic of locking, deadlock, and concurrency is an advanced topic not covered by this tutorial.
Let's Get Constructed
When you create an instance of an object using the key word "new", you call a class constructor. In fact, if you don't explicitly declare a class constructors, the compiler creates a hidden no argument constructor for you. Here is an example of explicitly declaring a no-arg do nothing constructor:
class Toaster
{
public Toaster() {} // this is a do nothing constructor
}
The compiler will create this constructor, if and only if, you do not declare any constructors for the class. The syntax for a public constructor is:
public NameOfClass(parameterList)
{
... stuff here
}
Let's Get Initialized
Constructors are often used to initialize the state of an object. Alternatively, you can initialize the instance fields when they are declared. Finally, you can break out the initialization code into a separate "Init" method. The following code demonstrates all three idioms for initializing an object:
/// <summary>
/// Summary description for Idioms
./// </summary>
class Idioms
{
private Hashtable idiom1= new Hashtable();
private Hashtable idiom2;
private Hashtable idiom3;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
//
// TODO: Add code to start application here
//
Idioms c= new Idioms();}
public Idioms()
{
Init();
idiom2= new Hashtable();
}
private void Init()
{
idiom3= new Hashtable();
}
}
Assigning an instance variable a value when you declare the variable is an example of defensive programming. It minimizes the chance of forgetting to initialize a variable in the constructor or Init method.
Creating Multiple Constructors
A common programming task is to create multiple constructors that differ only in their parameter list. The C# language supports this concept by allowing you to "overload" a method name. As long as the parameter list is sufficiently unique, you can create multiple methods or constructors with the same name.
Note: Be careful not to confuse overloading with overriding. Overriding a virtual method is quite different than overloading a method or constructor. Overriding is a subject of a future tutorial. I promise.
It's time to resurrect the Toaster class. Here is a new version of the Toaster class with two new instance fields that contain information about the color of the toaster and the model name of the toaster:
class Toaster
{
public const string DEFAULT_NAME= "Generic";
public enum ColorType {Black, Red, Yellow, White};
private static ColorType DEFAULT_COLOR= ColorType.Black;
private ColorType color= DEFAULT_COLOR;
private string modelName= DEFAULT_NAME;
}
Note the use of an enum "ColorType" to limit the domain of valid toaster colors. Here again is the default no-args constructor:
public Toaster(){} // black toaster with default name
The no-arg constructor simply leaves the default field values unaltered. You can now create a constructor that takes two parameters, the color type and the model name. Note that the constructor does validity checking to insure that the state of the object remains valid.
public Toaster(ColorType color, string modelName)
{
this.color= color;
if (modelName != null)
{
this.modelName= modelName;
}
}
You can now create a constructor that only takes one parameter, the model name:
public Toaster(string modelName)
{
if (modelName != null)
{
this.modelName= modelName
}
}
Now this looks like redundant code, just begging to be refactored. Happily, C# allows you to chain constructor calls, eliminating the redundant null checking code. You can chain the two constructors like this:
public Toaster(string modelName) : this(DEFAULT_COLOR, modelName) {}
The syntax is:
public ClassName(someParameters) : this(someParameters) {}
Pretty cool! By using C#'s built in support for constructor overloading and constructor chaining you can write a series of constructors. Here is the final version of the Toaster class using multiple overloaded constructors:
class Toaster
{
public const string DEFAULT_NAME= "Generic";
public enum ColorType {Black, Red, Yellow, White};
private static ColorType DEFAULT_COLOR= ColorType.Black;
private ColorType color= DEFAULT_COLOR;
private string modelName= DEFAULT_NAME;
public Toaster(){} // black toaster with default name
public Toaster(ColorType color, string modelName)
{
this.color= color;
if (modelName != null)
{
this.modelName= modelName;
}
}
public Toaster(ColorType color) : this(color,DEFAULT_NAME){}
public Toaster(string modelName) : this(DEFAULT_COLOR,modelName)
{}
public string Color
{
get
{
return Enum.Format(typeof(ColorType),
color,"G");
}
}
public string ModelName
{
get
{
return modelName; // danger, return
ModelName --> stack overflow!
}
}
}
What Is a Destructor?
C++ programmers are familiar with the concept of a destructor. I only briefly mention this topic here in self defense. In C++, a destructor is a method that is called when an object goes out of scope, is deleted, or the application closes. In C++, a destructor is often used to release valuable system resources. This works in C++ since memory reuse in C++ is deterministic. When an object in C++ goes out of scope, the destructor is called and resources can be released immediately.
Things are quite different in C#. First, memory reuse in C# is based on garbage collection. In a nutshell, when application memory becomes limited, the garbage collector executes and attempts to reclaim memory by reclaiming objects that are not "reachable". As a result, in C#, you cannot depend on a destructor to reclaim system resources in a timely manner.
Second, although C# supports the syntax of destructors, destructors in C# simply map to finalize. According to the IDE documentation:
~ MyClass()
{
// Cleanup statements.
}
... is converted by the compiler to:
protected override void Finalize()
{
try
{
// Cleanup statements.
}
finally
{
base.Finalize();
}
}
If your object uses valuable external resources, you may want your class to inherit from the IDisposable interface and implement the Dispose method, calling GC.SuppressFinalize. Alternatively, you want to rely on C#'s support for try, catch, finally to release external resources. For instance, you might want to open a connection in try and close any open connections in finally.
The concept of garbage collection and reclaiming external resources is definitely beyond the scope of this tutorial.
Using Static Factory Methods Instead of Multiple Constructors
You might wonder why I have chosen to combine the topics of static methods and constructors into a single chapter. The answer is "static factory methods." Instead of writing multiple public constructors, you can write multiple static factory methods and private constructors that return objects. First, here is an example of a static factory method. The method simply constructs an object with default values and then returns a reference to the object.
public static Toaster GetInstance()
{
return new Toaster(ColorType.Black, DEFAULT_NAME);
}
In a sense, this static method is analogous to the no-arg constructor.
public Toaster() {}
Here is the version of the Toaster class that uses static methods and a single private constructor to return toaster objects:
/// <summary>
/// Summary description for Toaster
/// </summary>
class Toaster
{
// static factory methods
public static Toaster GetInstance()
{
return new Toaster(ColorType.Black, DEFAULT_NAME);
}
public static Toaster GetInstance(string modelName)
{
return new Toaster(ColorType.Black, modelName);
}
public static Toaster GetInstance(ColorType color)
{
return new Toaster(color, DEFAULT_NAME);
}
public static Toaster GetInstance(ColorType color, string modelName)
{
return new Toaster(color, modelName);
}
public const string DEFAULT_NAME= "Generic";
public enum ColorType {Black, Red, Yellow, White}; // black is
the enum default value!
private static ColorType DEFAULT_COLOR= ColorType.Black;
private ColorType color= DEFAULT_COLOR;
private string modelName= DEFAULT_NAME;
// the single private constructor
private Toaster(ColorType color, string modelName)
{
this.color= color; // ColorType cannot be null --> compile
time error or defaults to ColorType.Black!
if (modelName != null)
{
this.modelName= modelName;
}
}
// the getters
public string Color
{
get
{
return Enum.Format(typeof(ColorType),
color,"G");
}
}
public string ModelName
{
get
{
return modelName; // danger, return
ModelName --> stack overflow!
}
}
}
Declaring the only constructor private, prevents any outside caller from directly instantiating the class. The only path to a Toaster object is through a static factory method. So, you can use multiple overloaded public constructors or multiple static factory methods and private constructors to create toaster objects. If you are interested in learning more about using static factory methods instead of multiple constructers check out Effective Java Programming Language Guide by Joshua Bloch, Addison-Wessley, 2001, 252 pp.
Note: The behavior of enum is quite complicated. You cannot set an enum to null and if you fail to explicitly initialize an enum variable, it defaults to the first member of the enumeration. For example:
public static ColorType c; // c --> ColorType.Black
Creational Patterns -- Class Factory and Singleton
I am going to finish off this chapter by introducing two common design patterns: the "Class Factory" and "Singleton" patterns. The class factory is useful when you want to return concrete objects that share a base class, at runtime. The singleton pattern is useful when you only want to allow the creation of one instance of an object in a application. These patterns are both considered "Creational" patterns since they abstract the creation of objects.
Using a Static Method to Return Concrete Classes -- The Class Factory.
The concept of using static methods to return objects is a useful one. In the previous code, you learned how to replace multiple constructors with multiple static factory methods. Another useful design pattern is the "Class Factory." A class factory can be used to return concrete implementations of a common base type.
In Chapter 2, you learned about polymorphism using the Drawable abstract class. Concrete implementations of the Drawable class such as square or circle provide a concrete implementation of the abstract "DrawYourself" method. Let's resurrect our Drawable class.
abstract class Drawable
{
public abstract String DrawYourself();
}
class Circle : Drawable
{
public override String DrawYourself()
{
return "Circle";
}
}
class Square : Drawable
{
public override String DrawYourself()
{
return "Square";
}
}
In this example of the class factory pattern, you pass a parameter to a static factory method that then returns the appropriate concrete implementation of the Drawable abstract class. To insure that the method is passed valid parameters at compile time, you can define a type safe enum "DrawableType":
public enum DrawableType {CIRCLE,SQUARE};
Here is our class factory:
/// <summary>
/// Summary description for class Factory.
/// </summary>
class Factory
{
public enum DrawableType {CIRCLE,SQUARE};
public static Drawable GetInstance(DrawableEnum e)
{
if (e == DrawableType.CIRCLE)
{
return new Circle();
}
else if (e == DrawableType.SQUARE)
{
return new Square();
}
else
{
throw new IndexOutOfRangeException();
// should never get here
}
}
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
//
// TODO: Add code to start application here
//
Drawable d1= Factory.GetInstance(Factory.DrawableType.CIRCLE);
Console.WriteLine(d1.DrawYourself());
Drawable d2= Factory.GetInstance(Factory.DrawableType.SQUARE);
Console.WriteLine(d2.DrawYourself());Console.ReadLine();
}
}
Note that d1 and d1 are reference variables of the Type Drawable, yet the code outputs: Circle, Square. Polymorphism at work! The class factory design pattern allows your application to create concrete implementations of a base class or interface dynamically at runtime in response to user or system input.
The Singleton Pattern
The "Singleton" pattern is a special version of the class factory that only returns a single instance of a class. The singleton pattern is useful when there should only be one instance of an object. As an example, there may be many soldiers that derive from person, but there should only be one reigning King of England that derives from person! Here is a sample that uses a static factory method to insure that only one instance is created. The factory method "GetInstance" returns a reference to the single instance of the class. Note that you must declare any constructors private, so that the constructors are not visible outside of the class. This insures that. there will be one and only one instance of MyClass.
/// <summary>
/// Summary description for MyClass.
/// </summary>
class MyClass
{
private static MyClass theOnlyOne= new MyClass(); // create one
and only one instance of the class
public static MyClass GetInstance()
{
return theOnlyOne;
}
public readonly string description= "The One and Only.";
private MyClass(){}
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
//
// TODO: Add code to start application here
//
MyClass mc= MyClass.GetInstance();
Console.WriteLine(mc.description);
Console.ReadLine();
}
}
One of the advantages of the factory method is that you can modify the singleton behavior of the class without affecting the caller of the class. If you decide that your application should now support Kings present and past, then you can modify MyClass to return a new instance for each call to GetInstance. Here is the modified multi-instance version of MyClass:
/// <summary>
/// Summary description for MyClass
/// </summary>
class MyClass
{
public static MyClass GetInstance()
{
return new MyClass();
}
public readonly string description= "OneOfMany";
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
//
// TODO: Add code to start application here
//
MyClass mc= MyClass.GetInstance();
Console.WriteLine(mc.description);
Console.ReadLine();
}
}
That's enough pain! I suggest the you jog around the block, clear your head and re-read this chapter later. In the next chapter, you will learn the top ten "gumption traps" for C++ and Java programmers.
Related articles
Related discussion
-
Help me how to dynamic create row column of TableLayoutpanel at run time ??????
by anatha1 (0 replies)
-
Very Urgent regarding deleting the images from a folder
by rameshbandi (2 replies)
-
How to Export Datagridview contents to Excel
by BarbaMariolino (8 replies)
-
Help accessing sound card
by daz4904 (0 replies)
-
How to Write a GPS Application
by stoyac (19 replies)
Related podcasts
-
Object-Oriented Programming in Ruby
In this episode, I talk with Scott Bellware about object-oriented programming in Ruby, and Ruby's object model. This is taken from a private conversation, and the audio quality suffers at times. Much thanks to Scott for allowing this to be released.This episode of the Alt.NET Podcast is bro...
thanks for the useful info
regards
If you have tried developing large extensible business applications where data needs are in the gigabytes if not terabytes in any static OO based programming language, then you are in for a lot of headaches. OOP does not lend itself well to this type of development as so called real world objects do not translate well into OO objects, most so called real world objects in the business world translate to data. This fact is lost on many managers, developers and analysts. Most applications in the real world are business related and not some academic project. I have been privy to many failed projects because of the selection of high level development language invariably they have always been statically typed. The one overlooked programming paradigm that has been neglected because of the OO hype is table or data driven programming. This form lends itself well to business related applications and can be used in conjunction with any programming paradigm but work wonders with a dynamically type languages. It is easily extensible, scalable and can be easily modified without throwing hundreds of users of the network.
As for Interfaces, function overloading, generics, templates, reflection, RTTI, default parameters, adding scripting etc, these are kludges to make a static language do what a dynamic language already does, and done badly at that.
When I first learned OO programming I was primarily excited by code re-use on the object side of the equation. Objects considered as providers of services to other code that acts as a client.
After many years of developing OO application I eventually grew to see the vast web of code dependencies and to realize that every piece of client code is itself serving some other code i.e. tier 1 calls tier 2 calls tier 3 (tier 2 code is being served by tier 3 but is serving tier 1). I realized that my objects provided a great deal of code re-use but what about all the code that used those objects? That is where Interfaces come in. Thanks to an interface all of your code that uses object A can be swapped out to use object B, C, D, or E regardless of the object type and therefore ancestry. Regardless of object implementation.
In another sense, Interfaces are more "fair". If an object can perform the services declared in the interface then it can be used by code that uses the interface. This means no strict binding to particular object hierarchies and that can save a lot of headaches.
Read the article even if you work with OO, give it to people that are just starting OO.
I'd really like to see this article as the core of explaining more OO topics, in the same format.
Workaround:
Save printable page as complete web page.
Edit default.css file in _files subfolder of saved page.
Change the CODE.Pre line to:
CODE.pre {
PADDING-RIGHT: 7pt; PADDING-LEFT: 7pt; WIDTH: 100%; COLOR: #000000; PADDING-TOP: 7pt; BACKGROUND-COLOR: #fbedbb
}
(removes the display:block)
RRIOS:
Thanks for the excellent write up on this concept. It has actually helped quite a bit to give me a better perspective on the subject of Interfaces. I particularly liked your analogy of Royal family vs democracy.
Thanks!
Inheritance is rigid. It´s like being born into the royal family. Sure, you get alot of privileges without doing anything, but you carry all the baggage and stigmas that come with your family name. You can´t JOIN the royal family. You have to be born into it.
Interfaces are democratic. A system that declares an interface is saying "I dont care who you are or how big or small you are: if you agree to these rules you may join the club". Think of your drivers license. The DVM declares an interface and ANYONE can use the road system as long as they stick to the rules (implement the interface).
In programming terms, suppose you are using two systems, in one you have created a Vehicle class and have several child classes like Car, Bicycle, and Bus. In the other you have cooking items, say for a recipe system. You may have classes like Flour, Eggs, Carrot. These two groups of classes have nothing to do with each other.
You create a third point of sale system for a retail store, and you plan to sell among other things, bicycles and flour. Your point of sale system could declare an IRetail interface that defines to other classes what they need to implement in order to participate in the Point of Sales system like Price(), TaxKey() and UPC().
This means you can reuse your Bicycle and Flour classes, just by adding the implementation of the IRetail interface in order to use them in the Point of Sale system. This new system will be capable of handling any kind of object as long as it implements IRetail.
So Interfaces are an advantage to both the system that declares the interface (they dont care what class of object they are handling) and to the classes that implement them (they can participate in any system as long as they agree to the rules).
http://www.jroller.com/page/haruki_zaemon/20031201#adapter_classes
An interface allows a developer, but more specifically a designer, to say "These features must be implemented." It is then up to the developer to work out the details of how to deliver on those features/abilities. It creates a boundary of expectation from other users of the object.
They can say "Oh I i see it implements IInterface, therefore it will definitely have X, Y and Z". It is quite useful when you wnat to implement concepts in multiple objects instead of concrete functionality. For instance, say you had two types of collections one was a tree and one was a jagged array of parent/child IDs (which in many ways is a tree represented another way but they are syntactically different). If you wanted to implement something like "GetParent", you couldn't (well maybe you could, but go with me for the sake of example) implement one generic function for "GetParent" that would work for both the Jagged Array and the Tree. YOu could however have both objects implement an interface, and then have the developer do the syntactic details of returning an items parent.
Anyway, more half baked analogies.
One of the questions that still persists for me, (and something I have never seen a discussion on) is what real benefit Interfaces offer; it seems they offer some, but that it is somewhat limited. Let me give an analogy, in the form of an old joke. An engineer, a phycicist and an economist were on a desert island with a can of pineapple that had survived the boat wreck. Both the engineer and the physicist made multiple attempts using their scientific knowledge to open the can of pineapple, but to no avail. After some time the economist remarked: "This shouldn't be a difficult problem to solve." "Well, tell us", replied the engineer and physicist in unison. Said the economist, " Assume a can opener."
This is what economists do. They make assumptions and think they have contributed something. Is this not like Interfaces, whose job, it appears, is to make policy statements without ever getting around to implementing anything? All the same amount of coding has to be done whether the interface is used or not; in fact there may be considerable duplication of code if minor changes to an implementation are needed. So what has been achieved? Not nearly as much, it seems, as inheritance would offer, were full inheritance of pure methods possible. This is perhaps why an Abstract class seems to be more useful.
I would appreciate anyone who can resolve my perplexity here. I could learn something.
Pretty good article. Its the first time i acutally laughed and smiled while reading your article. Very good read..
There are a few inaccuracies though just to point out...
1.
MyClass c;
c.someMethod()
does not throw a null reference exception. It actually throws a compiletime error "Use of unassigned local variable". In c# variables much be assigned before use or something like that..
2.
array[] = new array[10]
only returns an array of null references if it you are declaring an array of ref type. If it is a value type, the values will be assigned their default values..
and 3.
comparing type compatibility in most cases should use "as" instead of "is" because if you use is before casting the type, you in effect check the type twice, once before the cast, and once during the cast..
Its just an optimisation really..
Other than that, like i said before, best read i have ever had. Learned a few new words too like "gumption". Who would have thought..
This intro to C-Sharp is a very good tutorial, but its only an intro. The naming of variables and methods need to be standardized for easier readability. It also does not cover data driven dynamic web apps.
...it is a good article and Microsoft does advocate clearer naming of Object variables.
It's a shame you never LEARNED proper grammer.
This thread is for discussions of A Twisted Look at Object Oriented Programming in C#.