Since .NET3 there is a significant change in how Microsoft,”partners” and open source projected have delivered their assemblies. Until .NET2 there was a lot of usage of constructors with parameters but the introduction of WPF,WCF,WF and LINQ prety much forced the definition of types with parametless constructors.
.NET even introduced Property setting just after the construction to help move to a parametless constructors world. Who doesn’t remember all these problems with the specific constructor for deserialization. Funny errors appeared when for any reason any object was serialized and deserialized inside an engine like Workflow Persistent service.
Personally I never felt comfortable with constructors with parameters. I have always been writing framework code with heavy use of delegates and dynamic code generation. Parameters in the constructions never helped assemblies with this kind of types. But there is always the case of requiring constructors with parameters. There is no official best practises approach for this matter but here is what I think about this.
I make the following distinctions on the whys and why nots for constructors overloading.
The don’t to it cases:
- A class instance is too volatile to leave its functionality to not properly and guided initialization. This can be solved in two ways. Either use a bunch of Guard methods inside every call or use static instance creators. The good thing about this approach is that the static creators are actually functions that can differ in signature and name. The name is very important because usually this kind of classes have serious variations based on the input parameters. In this case overloading the functions doesn’t help and proper naming guides the code and the developer better. Also different function names help a lot in a stack trace because you can clearly see the function name that crashed when the constructors by design all have the same name. Stack trace isn’t good at revealing for which of the overloaded functions or constructors were used, so you need always to be very careful when deciding to overload.
- The class can be really initialized with totally different ways. This means that the constructors will execute different code and probably won’t end up at a common code.This kind of cases usually reflect the wish of the developer to provide a helper to properly initialize his class for various different scenarios that he can think of. But in the spirit of code decoupling I strongly believe that these “helpers” should not be constructors but static functions probably with different names. This case resembles the first one and one can argue that is looking into a mirror from a different angle. The reason I differentiate this case, is because it is similar to most of the IO and XML type initializers that come with .NET. All of them target the same functionality but for different channels, like files, streams in memory streams and so forth. Most of the classes provide static instance creators overloaded or with different names depending on the situation. The reality is that in order for a class like this to get to the same state ready to be used different things need to happen with a variety of errors. For example a file can not exist. But end result is having an instance that you know is ready and stable to use.
- The classic service initialization paradigm from MVC controlers and services examples. Here a class really needs to execute the same logic on some private members but it allows a different initial values for these fields. Common characteristic for such cases is that all constructors end up on the same code through the use of the this keyword. This means that more or less the class is constructed the same way because it ends up doing so with the same piece of code. The overloading of constructors here gives a flexibility but it never adds functional change. And always keeps the default constructor available.
- The most important one for me is if the class has a persistable state. If you feel that for any reason the class acts as a container for data then a default constructor is at least required. If the class could for any reason be required to be persisted in the future then default constructor becomes even more important. Even a service-like class should have a default constructor because even if it isn’t holding data it is holding a reference to something the provides functionality.These service-like classes can be easily initialized from engines like WCF and unit testing.
As a rule of thumb I would suggest that constructors with parameters should never be used as a means of converting a variety of external arguments to an internal set. The conversion should always exist outside the scope of the constructor for better error handling and code decoupling.
Additionally I believe that type instance initialization should be as fast as possible and any heavy operation (that has the potential to go wrong) should be delivered though some sort of function. Don’t forget that constructors cannot be unit tested.
To summarize, I would only use additional constructor to the default one for two major reasons.
- Mock the provided that would normally be created. See MVC controller and service examples.
- Provide some slight variations of the default constructor. This variation never changes the creation process and it is mandatory for all constructors to use the same piece of code. Unit testing shouldn’t differ and these variations should be applicable also by property setting, thus allowing same unit testing patterns. But setting properties on the same command as with the constructor really makes this option is becoming obsolete.