Don't forget that C# constructor overloads and method overloads work in opposite ways

Published 02 February 05 08:13 PM | alexcampbell

It's 9pm and I'm using the quiet time in the office to write some unit tests for our new CMS product.  This process just uncovered an interesting bug:

1   public class SecurityManager  {

2    

3      public SecurityManager () {

4             

5         if(user == null) {

6            this._user = User.CurrentUser;

7         }

8

9        InitialiseData();

10

11      }

12   

13      public SecurityManager(User user) : this() {

14

15         this._user = user;

16 

17      }

18 

19      private User _user;
20

21   }

 

The static method User.CurrentUser returns the currently logged in user (using HttpContext.Current).  This code was working fine in the web application but the unit tests were failing (throwing a NullReferenceException).  The stack trace showed that the exception was being thrown from Line 6.  But NUnit was calling the second constructor with a non-null User object.  Line 6 shouldn't even be hit.  I was confused until I remembered the order of execution for C# constructors.

      public SecurityManager () {

           

            Console.WriteLine("This will be executed first");

 

      }

     

      public SecurityManager(User user) : this() {

 

            Console.WriteLine("This will be executed second");

 

      }

The fix was simple:

public class SecurityManager  {

 

      public SecurityManager () : this(null) {

 

      }

     

      public SecurityManager(User user) {

 

            this._user = user;

            if(user == null) {

                  this._user = User.CurrentUser;

            }

 

            InitialiseData();

 

      }

 

      private User _user;

}

I got confused because I expected the constructor I called to be run first, just as method overloads work.  In fact, constructor overloads work in the opposite way to method overloads:

      public void CreateSecurityManager() {

            Console.WriteLine("This will be executed second");

      }

 

      public void CreateSecurityManager(User user) {

            Console.WriteLine("This will be executed first");

            this._user = user;

            CreateSecurityManager();

      }

What a stupid mistake!  I've just checked the last couple of classes I've written and they were correct - I guess the coffee was wearing off when I wrote this class.

I like blogging about embarressingly obvious mistakes like this because it means I'll never make the mistake again.  :-)

Comments

# Geoff Appleby said on February 2, 2005 05:48 AM:

Scary. It's for this reason that I consider ': this()' to be the actual first line of a C# constructor - that way, i don't confuse myself.

Dare I say it, but this is one spot where I think the VB syntax definately removes confusion:

Public Sub New(something as sometype)
Me.New()
'do other stuff
End Sub

It's also annoying, because you can only do Me.New() or MyBase.New() (for inherited classes) but not both. Can you in C#?

# Alex Campbell said on February 2, 2005 05:53 AM:

Geoff - I love your blog! CodeBetter rocks.

As far as I'm aware, there's no C# equivalent to Me.New() or MyBase.New().

> I consider ': this()' to be the actual first
> line of a C# constructor
That is a great way of looking at it - I'll keep that in mind.

# Geoff Appleby said on February 2, 2005 05:58 AM:

Thanks man :)

Well, there sorta is. In a C# constructor you put before the curly bracket (and i cut'n'paste from your blog entry :)

: this()

and

: this(null)

which is directly calling a different constructor overload before entering the current overload.

My C# is very rusty, but isn't it also possible to have

: base()

or

: base(null)


?? That's directly equivalent to Me.New and MyBase.New :) However, in C# can you do this:

public class SomeClass : SomeOtherClass
{
public SomeClass(someType someParam) : this(), base(someParam)
.....
....
etc :)

I waffle. I waffle way too much :)

# Eugenio said on February 2, 2005 06:06 AM:

This is why we talk about "constructor chaining" and not "overloading".

# Amos Cividalli said on February 24, 2005 03:00 AM:

Hello
Regarding the order of execution in overloaded methods, I am not sure you are right. I think this depends on the order in which you call the other overloads, if you call them at all. In case your second overload is something like this:
<div style="margin: 20pt;font-family: 'Courier New';font-size: 10pt;">
<span style="color:blue">public void</span> CreateSecurityManager(User user) {

CreateSecurityManager();

Console.WriteLine("This will be executed first");

<span style="color:blue">this</span>._user = user;

}

</div>
I think that the second line will be written first, and in case you ommit the line:
<div style="margin: 20pt;font-family: 'Courier New';font-size: 10pt;">

CreateSecurityManager();

</div>
I assume it will never be written.

In this regard, I would like to post here a question in this subject I have also posted yesterday at the Code Project site, but was not yet answered.
Is there a way I can call another constructor with some calculated parameters (that cannot fit into one line)? let's say the one overload of the constructor received a user object as a parameter, and another one may receive only name, create a new user object and than call the "regular" constructor. I have noticed that I can write (and run) something similar to the following code:
<div style="margin: 20pt;font-family: 'Courier New';font-size: 10pt;">
public mySampleClass(string usr_name): this(new UsrObj(usr_name)) {}

<span style="color:blue">public</span> mySampleClass(UsrObj usr)
{
<span style="color:green">
// This is the actual constructor method.
// Do some code. </span>
}
</div>
In your case you could of course use:
<div style="margin: 20pt;font-family: 'Courier New';font-size: 10pt;">
<span style="color:blue">public</span> SecurityManager () : <span style="color:blue">this</span>(User.CurrentUser)
</div>
But what if creating a new UsrObj needs more than one line of code (e.g. updating a DB or saving data somehow)? Can it be done?

I hope someone here can answer my question.
Thanks a lot, Amos

# Amos Cividalli said on February 24, 2005 03:02 AM:

Oopsss, the HTML I have worked hard to enter in my comment did not render as HTML, please try reading between the tags.