James Michael Hare

...hare-brained ideas from the realm of software development...
posts - 166 , comments - 1474 , trackbacks - 0

My Links

News

Welcome to my blog! I'm a Sr. Software Development Engineer in the Seattle area, who has been performing C++/C#/Java development for over 20 years, but have definitely learned that there is always more to learn!

All thoughts and opinions expressed in my blog and my comments are my own and do not represent the thoughts of my employer.

Blogs I Read

Follow BlkRabbitCoder on Twitter

Tag Cloud

Article Categories

Archives

Post Categories

Image Galleries

.NET

CSharp

Little Wonders

Little Wonders

vNext

C#/.NET Little Wonders: Cross Calling Constructors

Just a small post today, it’s the final iteration before our release and things are crazy here! 

This is another little tidbit that I love using, and it should be fairly common knowledge, yet I’ve noticed many times that less experienced developers tend to have redundant constructor code when they overload their constructors.

The Problem – repetitive code is less maintainable

Let’s say you were designing a messaging system, and so you want to create a class to represent the properties for a Receiver, so perhaps you design a ReceiverProperties class to represent this collection of properties.

Perhaps, you decide to make ReceiverProperties immutable, and so you have several constructors that you can use for alternative construction:

   1: // Constructs a set of receiver properties.
   2: public ReceiverProperties(ReceiverType receiverType, string source, bool isDurable, bool isBuffered)
   3: {
   4:     ReceiverType = receiverType;
   5:     Source = source;
   6:     IsDurable = isDurable;
   7:     IsBuffered = isBuffered;
   8: }
   9:     
  10: // Constructs a set of receiver properties with buffering on by default.
  11: public ReceiverProperties(ReceiverType receiverType, string source, bool isDurable)
  12: {
  13:     ReceiverType = receiverType;
  14:     Source = source;
  15:     IsDurable = isDurable;
  16:     IsBuffered = true;        
  17: }
  18:  
  19: // Constructs a set of receiver properties with buffering on and durability off.
  20: public ReceiverProperties(ReceiverType receiverType, string source)
  21: {
  22:     ReceiverType = receiverType;
  23:     Source = source;
  24:     IsDurable = false;
  25:     IsBuffered = true;        
  26: }

Note: keep in mind this is just a simple example for illustration, and in same cases default parameters can also help clean this up, but they have issues of their own.

While strictly speaking, there is nothing wrong with this code, logically, it suffers from maintainability flaws.  Consider what happens if you add a new property to the class?  You have to remember to guarantee that it is set appropriately in every constructor call.

This can cause subtle bugs and becomes even uglier when the constructors do more complex logic, error handling, or there are numerous potential overloads (especially if you can’t easily see them all on one screen’s height).

The Solution – cross-calling constructors

I’d wager nearly everyone knows how to call your base class’s constructor, but you can also cross-call to one of the constructors in the same class by using the this keyword in the same way you use base to call a base constructor.

   1: // Constructs a set of receiver properties.
   2: public ReceiverProperties(ReceiverType receiverType, string source, bool isDurable, bool isBuffered)
   3: {
   4:     ReceiverType = receiverType;
   5:     Source = source;
   6:     IsDurable = isDurable;
   7:     IsBuffered = isBuffered;
   8: }
   9:     
  10: // Constructs a set of receiver properties with buffering on by default.
  11: public ReceiverProperties(ReceiverType receiverType, string source, bool isDurable)
  12:     : this(receiverType, source, isDurable, true)
  13: {
  14: }
  15:  
  16: // Constructs a set of receiver properties with buffering on and durability off.
  17: public ReceiverProperties(ReceiverType receiverType, string source)
  18:     : this(receiverType, source, false, true)
  19: {
  20: }

 

Notice, there is much less code.  In addition, the code you have has no repetitive logic.  You can define the main constructor that takes all arguments, and the remaining constructors with defaults simply cross-call the main constructor, passing in the defaults.

Yes, in some cases default parameters can ease some of this for you, but default parameters only work for compile-time constants (null, string and number literals).  For example, if you were creating a TradingDataAdapter that relied on an implementation of ITradingDao which is the data access object to retreive records from the database, you might want two constructors: one that takes an ITradingDao reference, and a default constructor which constructs a specific ITradingDao for ease of use:

   1: public TradingDataAdapter(ITradingDao dao)
   2: {
   3:     _tradingDao = dao;
   4:  
   5:     // other constructor logic
   6: }
   7:  
   8: public TradingDataAdapter()
   9: {
  10:     _tradingDao = new SqlTradingDao();
  11:  
  12:     // same constructor logic as above
  13: }

 

As you can see, this isn’t something we can solve with a default parameter, but we could with cross-calling constructors:

   1: public TradingDataAdapter(ITradingDao dao)
   2: {
   3:     _tradingDao = dao;
   4:  
   5:     // other constructor logic
   6: }
   7:  
   8: public TradingDataAdapter()
   9:     : this(new SqlTradingDao())
  10: {
  11: }

 

So in cases like this where you have constructors with non compiler-time constant defaults, default parameters can’t help you and cross-calling constructors is one of your best options.

Summary

When you have just one constructor doing the job of initializing the class, you can consolidate all your logic and error-handling in one place, thus ensuring that your behavior will be consistent across the constructor calls.

This makes the code more maintainable and even easier to read.  There will be some cases where cross-calling constructors may be sub-optimal or not possible (if, for example, the overloaded constructors take completely different types and are not just “defaulting” behaviors).

You can also use default parameters, of course, but default parameter behavior in a class hierarchy can be problematic (default values are not inherited and in fact can differ) so sometimes multiple constructors are actually preferable.

Regardless of why you may need to have multiple constructors, consider cross-calling where you can to reduce redundant logic and clean up the code.

 

 Technorati Tags: ,,

 

Print | posted on Thursday, December 16, 2010 6:24 PM | Filed Under [ My Blog C# Software .NET Little Wonders ]

Feedback

Gravatar

# re: C#/.NET Little Wonders – Cross Calling Constructors

With optional parameters these things are not required anymore. You can define one constuctor with all the parameters and then just use optional and default values to set the values.

-RN
3/9/2011 1:17 PM | rn
Gravatar

# re: C#/.NET Little Wonders – Cross Calling Constructors

@RN:

Not quite true. First of all default values only work in .NET 4.0, not before. Also default parameter values have a set of issues all their own when you deal with inheritence and interface implementation (defaults aren't inherited).

Finally, default parameter values ONLY work for compile-time constants. You can't use it with non-string reference types other than null. Thus, you still need cross-calling constructors to do anything with appreciable complexity.
3/9/2011 2:13 PM | James Michael Hare
Gravatar

# re: C#/.NET Little Wonders: Cross Calling Constructors

there is another situation, where cross-calling constructors are still needed: think about copy-constructors...
6/25/2012 8:35 AM | TST
Post A Comment
Title:
Name:
Email:
Comment:
Verification:
 

Powered by: