CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Raymond Lewallen

Framework Design, Agile Coach, President Oklahoma City Developers Group, Microsoft MVP C#, TDD, Continuous Integration, Patterns and Practices, Domain Driven Design, Speaker, VB.Net, C# and Sql Server

Avoid breaking your API - pay attention to your constructors.

I was playing with a document coverter API a few months ago to do some PDF to Word conversions.  I had some code already built into a console app to use that version and it was working fine.  Last night I went and downloaded their latest version, and wouldn’t you know, it commited one of the major crimes of APIs: it broke my code.

The culprit?  The constructor to a class.  It used to work as follows:

Converter fileConverter = new Converter();
fileConverter.Path = “c:\myfiles\test.pdf”;

Now, for whatever reason, they decided to change the functionality so that you pass the file path into the constructor like so:

Converter fileConverter = new Converter(filePath);

Now, there is nothing wrong with this, except for one major thing they overlooked.  On a class, if you have a parameterized contructor, the comiler WILL NOT emit a default parameterless constructor.  Only when the class is void of any constructor at all will the compiler emit a default parameterless constructor.  When they created the parameterized constructor, they should have put in another constructor so that breaking changes wouldn’t occur:

public Converter() {}

The Path property is still read/write, so simply adding the parameterless constructor would have left my code working fine.  Please, avoid the same mistake.  This would have avoided them shipping their API with breaking changes and help them to avoid some pissed off customers who rely on their product.



Comments

johnwood said:

Yeah I think that's pretty annoying actually. Most of the time you *don't* want to hide the default constructor. It's a bit counter-intuitive that declaring a new constructor deletes another behind your back. They should have come up with a new syntax to remove the default constructor, so you can remove it if you want to. What you're describing here has happened to me many times.
# May 11, 2006 11:05 AM

Brendan Tompkins said:

I agree. I guess the problem would be that you'd have to invent some new way to remove the default constructor through attributes or something, or explicitly always require a default constructor.. Either way seem like big language changes to me.  Better probably to just have the annoyance and sweep the issue under the rug.
# May 11, 2006 11:31 AM

Raymond Lewallen said:

They should have required explicit declaration of all constructors, always, instead of letting the compiler pick and choose when its going to do the work for you.  No getting away from that now, as a change like that would break so many things its unfathomable.
# May 11, 2006 11:36 AM

DaRage said:

Nice tip thank you. I love those kind of little things that differentiate a good developer from bad one.
# May 11, 2006 2:37 PM

TobinTitus said:

Bless you for posting some code into your blog!  It really is refreshing to see someone still has a good idea of what codebetter was always about -- CODE. :)
# May 11, 2006 5:58 PM

Piotr Czarnas said:

There will be even more libraries for .NET 2.0 that fail for the same reason. The default new class template in Visual Studio .NET 2003 was a public class with an empty default constructor. In Visual Studio .NET 2005 a new class created from a template is internal (I am not afraid about the protection level) and has no default constructor!
When API authors will create their libraries in VS.NET 2005 we will see probably more examples of this problem.
# May 14, 2006 11:13 AM

johnwood said:

You know something just occurred to me. A good way to solve this is if C# supported optional / defaultable parameters. That way if you define a new constructor but you make the fields optional or with default values, then the compiler would be able to automatically generate a parameterless constructor for you that delegates to the new one. Wouldn't that be a good solution to this problem?
# May 16, 2006 3:23 PM

TobinTitus said:

John, you can already pull that off in a few ways. In many cases, the default constructor is used to fill in default values within the constructor implementation. There is also an option to do something like this:

class Converter  {  
   string m_FilePath = null;

   public Converter() :
            this( @"c:\myfiles\test.pdf" ) {
   }
   public Converter( string filePath ) {
        m_FilePath = filePath;
   }
}

Obviously, if you use the default contructor, you'll pass the default value of "C:\myfiles\test.pdf" to the second constructor which will populate the field.  This is also a simple example. You should likely check your parameter for null values if you don't want to accept them.  Whatever the case, you can always default the value, but Raymond's point was that they didn't do any of that.  They broke their compatibility from one release to the next and that's the real issue.
# May 16, 2006 6:18 PM

johnwood said:

Sorry Tobin I (think) you're missing my point... I was pondering on how they could have changed the C# language so that defining a parametered constructor wouldn't automatically delete the default constructor. We were discussing this a little above, and raymond pointed out that it's a difficult thing to do without breaking compatibility. My suggestion was that, if parameters could be defaulted, then the compiler could automatically create a dummy default constructor (hidden) that calls your constructor with all parameters 'missing' and defaulted. This would have solved the problem raymond was initially seeing and would improve the language IMO.
# May 16, 2006 6:23 PM

Raymond Lewallen said:

Therein lies where VB has an advantage, because you can declare the following:

Public Class Test

   Public Sub New(Optional ByVal path As String = "c:\path\file.pdf")

   End Sub

End Class

Public Class Test2
   Private a As New Test
End Class

And get both constructors simulated.  I say simulated, because there really isn't a parameterless constructor, it just has a default value.  So if this style was implemented into the PDF/Word converter library, the API would still have a big problem, because behind the scenes the Path property is still being set by the constructor, and if you don't set it, everything will run fine until it tells you it can't find your file because and exception for ArgumentNullException wouldn't be thrown by the Convert method because the Path property is actually set with a value.  For me, it would have worked out fine because my existing code sets the property.  New users to the API may run into this problem though.

So its a major catch 22 situation and a sticky one to put yourself into.  Optional parameters are not recommened, except for very strict situations, because of this type of breaking behavior that can happen.
# May 16, 2006 8:31 PM

johnwood said:

Ah I hadn't thought of the fact it would implicitly work in VB because of its support for optional parameters. And of course it wouldn't work if you used the API from, say C# or another language that didn't support optional parameters.

In your example, though, wouldn't the default value be null rather than a hard-coded file? And if it's null, wouldn't that emulate the same behavior as the original parameterless constructor?
# May 16, 2006 11:26 PM

Raymond Lewallen said:

John, yes, if the parameter were in fact null (Nothing in VB) then yes, I think the desired behavior of avoiding the API breaking would be achieved.
# May 17, 2006 3:47 PM

johnwood said:

You know I seem to hear more and more good reasons to use VB. In some ways it's a shame the language has a stigma attached.
# May 17, 2006 9:35 PM

Randy said:

Huh, I've been having the opposite problem in Visual Studio 2005, the compiler throwing in a parameterless constructor when I've already defined a parametered one, and I don't WANT a default constructor.  I guess that does fix the API breaking you mentioned, but it's obnoxious to have to manually put in a private New method just to keep people from calling it.

Especially when the compiler gives you a public constructor for an abstract class and Visual Studio complains about that.
# August 16, 2006 4:04 PM

pdf converter said:

great blog!

i have known much about pdf from your blog!

thanks!
# August 21, 2006 9:18 PM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add

About Raymond Lewallen

Working primarily in the public sector during his career, Raymond has designed and built several high profile enterprise level applications for all levels of the government. Raymond now works as a solutions architect for EMC. Raymond is an agile coach, Microsoft MVP C# and also president of the Oklahoma City Developers Group and Oklahoma Agile Developers Group. Raymond spends a lot of his time learning and teaching such things as Test Driven Development, Domain Driven Design, Design Patterns and Extreme Programming practices and principles, to name a few. Raymond is also an advocate of Alt.Net. Raymond is primarily a framework guy, so don't ask him anything about UI :) Check out Devlicio.us!