You need to force a client to use an overloaded constructor, which accepts parameters to fully initialize the object, rather than a default constructor, which may not fully initialize the object. Often a default constructor cannot fully initialize an object since it may not have the necessary information to do it. Using a default constructor, the client is required to perform a multistep process; for instance, create the object and then initialize its fields either through various properties and/or methods.
By removing the default constructor
and strictly using parameterized constructors, the client is forced
to provide the necessary initialization parameters during object
creation. The following Log
class will not
initialize its LogStream
field to a
StreamWriter
object on
construction:
public class Log { private StreamWriter logStream = null; public StreamWriter LogStream { get {return (logStream);} set {logStream = value;} } // use the LogStream field... public void Write(string text) { logStream.Write(text); } }
The C# compiler will automatically create a default constructor that calls the default constructor of its base class, if you omit the constructor for a class. The following modified class will prevent the default constructor from being created:
public class Log { public Log(StreamWriter logStream) { this.logStream = logStream; } private StreamWriter logStream = null; public StreamWriter LogStream { get {return (logStream);} set {logStream = value;} } // use the LogStream field... public void Write(string text) { logStream.Write(text); } }
When a client creates an object from this class, the client is forced
to initialize the LogStream
field.
There is a small problem with not supplying a default constructor. If
a class inherits from Log
and does not supply a
constructor of its own, the C# compiler will produce the rather
cryptic error “No overload for method
`Log’ takes
`0’ arguments.”
The following class produces this
error:
public class EnhancedLog : Log { public EnhancedLog (string s) { // Initialize... } }
What this means is that Log
does not contain a
default constructor. The C# compiler automatically adds a call to the
base class’s default constructor, if you do not
specify otherwise. Therefore, the EnhancedLog
constructor contains an unseen call (this call can be seen using
Ildasm) to the default constructor of the Log
class.
This problem can be solved in one of several ways. First, we could
simply add a protected default constructor to the
Log
class. This would prevent the creation of a
Log
object using the default constructor, but
would allow classes inheriting from Log
to do so
without problems. A second method is to use the
base
keyword to direct the constructor to call a
particular constructor in the base class. The following
EnhancedLog
class uses the base
keyword to call the parameterized constructor of the base
Log
class, passing in a
StreamWriter
object:
public class EnhancedLog : Log { public EnhancedLog (string s) : base(new StreamWriter(@"C: est.log")) { // Initialize... } }
A third way to solve this problem is to make the
Log
class noninheritable by adding the
sealed
keyword to the class declaration. While
this prevents the problem of calling the default constructor, it also
prevents others from inheriting from and extending the
Log
class. For many cases, this third solution is
not the best one.