A Portion of Buff

Everybody else had one, so...

Do you know what your constructor is doing?

Earlier, Peter Torr posted about the differences between language-enforced rules and framework-enforced rules (calling class constructors, specifically).  An interesting and related thing I discovered a couple of weeks ago was that constructors are not guaranteed to be called at all by the framework, let alone twice.  Again, it involves emitting MSIL (or not, as we shall see), rather than C#, but it's still a useful/dangerous/so-what feature.

Here's the MSIL that CSC emits by default for an empty constructor:

ldarg.0
call       instance void [mscorlib]System.Object::.ctor()
ret

As you can see, the base constructor is automatically called.  This call, as CSC would have it, happens before anything else in the current constructor is executed.  The trick - you guessed it - is simply to omit this call.  A constructor then becomes:

ret

Marvellous.  And what's the big deal?  Well, it turns out to be a solution to a limitation I keep running into with NMock, namely that it cannot mock a class without a parameterless constructor (NMock creates testable sub-classes on the fly using Reflection.Emit).  I got so fed up of creating single-implementation interfaces or empty constructors just to mock a class that I decided to investigate, and that's what I came up with.  If constructing the base class is a chore (because of the parameters it takes), then don't call it.  If it doesn't have an empty constructor, so what?  I don't have to call it.

You do have to be a bit careful calling unmocked methods who rely on state that should have been initialised by the constructor, but in certain situations it makes life much much easier.

The code should make it into NMock in the next couple of weeks, if I can get my arse in gear.

Comments

Raymond Chen said:

Another place where the constructor is bypassed (and doesn't required custom IL generation) is in deserialization:

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using SC = System.Console;

[Serializable]
class Program {
Program() { SC.WriteLine("constructed"); }
public static void Main() {
SC.WriteLine("Creating");
Program p = new Program();
MemoryStream stm = new MemoryStream();
IFormatter fmt = new BinaryFormatter();
SC.WriteLine("Serializing");
fmt.Serialize(stm, p);
stm.Seek(0, SeekOrigin.Begin);
SC.WriteLine("Deserializing");
Program q = (Program)fmt.Deserialize(stm);
SC.WriteLine(p == q);
}
}

This program prints


Creating
constructed
Serializing
Deserializing
False

there are two Program objects (p and q), but only p got its constructor run. q was created without its constructor ever having run.
# January 27, 2004 11:42 PM

Jim Arnold said:

"Why not using POCMock for mocking your .NET classes?"

I've looked at POCMock. I even started writing something that did exactly the same thing (before I saw POCMock) because I was frustrated with NMock.

Then I started using mocks properly, and understood that they're a design tool as well as a testing tool. I think static mocks (powerful as they are) subvert the design benefits of mocking. They're great for classes that just can't be mocked (and we can now mock classes with constructors with NMock), but it's a bit heavy-duty for general usage.

Also, I can't see any way to have both a mocked instance and an original instance of the same class in one test. Is that possible with POCMock?

It also costs money and is closed-source :-)
# February 4, 2004 11:53 AM

Reza said:

Raymond:

Actually it is very smart that it doesn't call the constructor for q.
Consider you example, with a little change:

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

[Serializable]
class Program
{
public int i = 0;
Program(string dummy)
{
++i;
Console.WriteLine("constructor is called");
}

public static void Main()
{
Console.WriteLine("Creating");
Program p = new Program(" somthing");
MemoryStream stm = new MemoryStream();
IFormatter fmt = new BinaryFormatter();
Console.WriteLine("Serializing");
fmt.Serialize(stm, p);
stm.Seek(0, SeekOrigin.Begin);
Console.WriteLine("Deserializing");
Program q = (Program)fmt.Deserialize(stm);
Console.WriteLine(p==q);
Console.WriteLine("p.i = {0}", p.i);
Console.WriteLine("q.i = {0}", q.i);
}
}


I believe the p and q are the same object, I don't know why p==q return false!, if the constructor of q was supposed to be executed then what was the point of deserilizing? because your object is lost its state and when you deserialize you will get a different result.
# February 25, 2004 6:20 PM