1

Well my question is pretty self-explanatory. I have a class and I want to ensure that there is just 1 public constructor to this class. Moreover, I also want to ensure that the constuctor should have just 1 parameter. My class will be modified by many different developers, and I am looking for a way to ensure that they do not write any more constructors than are currently specified. Is it possible? If yes, then how?

Note, my class inherits from another class which currently does not have any constructor but it might have in the future. I don't know if this information will affect the answer or not but just thought of adding it.

Please help! Thanks in advance!

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
samar
  • 5,021
  • 9
  • 47
  • 71
  • 4
    Why do you want to restrict the class to one constructor? Describing the actual problem you want to solve might get you better answers. – Georg Fritzsche Feb 02 '11 at 09:53
  • Could you clarify what you mean by "ensure"? – Ani Feb 02 '11 at 09:54
  • 2
    *my class inherits from another class which currently does not have any constructor* - This is not true. Every class has a constructor, whether you write one explicitly or not (if you don't write one it gets a default constructor). – Mark Byers Feb 02 '11 at 09:58
  • 2
    I'd personally go with source control and code review as the best method of making sure other developers don't do the wrong thing to the code. If they check in things against the reccomendation you pick it up in a review and roll back their changes. If they have compelling reasons you look embarrassed and change your rules. :) – Chris Feb 02 '11 at 10:39

7 Answers7

4

You could consider writing a unit test to encode this design constraint. As long as the test isn't fiddled with, this will warn when the contraint is broken.

This would be a good case for a nice comment in your class detailing this constraint.

The following testing approach can be expanded to provide a test which could test derived types, rather than a single type. This approach is a type of static analysis, and removes the overhead that would be incurred by expensive runtime checking through reflection for instance. A test ensures that the design constraint is validated at build time, rather than at runtime which could be after code is released.

[Test]
public void myClass_must_have_one_single_paramter_ctor()
{
    Type type = typeof(MyClass);

    const BindingFlags Flags = (BindingFlags.Public | BindingFlags.Instance);

    ConstructorInfo[] ctors = type.GetConstructors(Flags);

    Assert.AreEqual(1, ctors.Length, "Ctor count.");

    ParameterInfo[] args = ctors[0].GetParameters();

    Assert.AreEqual(1, args.Length, "Ctor parameter count.");

    Assert.AreEqual(typeof(string), args[0].ParameterType, "Ctor parameter type.");
}

public class MyClass
{
    public MyClass(string woo) {}
}
Tim Lloyd
  • 37,954
  • 10
  • 100
  • 130
  • 1
    +1 - the only sensible way to do this is at build time - either a unit test, or perhaps a code analysis rule. – Joe Feb 02 '11 at 12:22
  • Where should i add this method? I have never added such methods. Please guide me. – samar Feb 02 '11 at 12:25
  • @samar Do you have a build system for your project? – Tim Lloyd Feb 02 '11 at 12:27
  • @samar You have **many** developers, and no build system - and by the looks of it no unit tests either - ok. A build system is responsible for performing automated builds of your software. As well as compiling your software, it is typically responsible for running other tasks such as test suites. In this way changes made by members of the development team are automatically and continuously integrated into the product and built and usually at a minimum automatically tested (the code above is an example of a test). These days it's pretty standard practice (I hope) and a basic quality hallmark. – Tim Lloyd Feb 02 '11 at 12:39
  • @samar A good example of software that can be used to create a build system is [TeamCity](http://www.jetbrains.com/teamcity/index.html). I am an experienced build master and strongly recommend it. A good example of a unit testing framework is [NUnit](http://www.nunit.org/). – Tim Lloyd Feb 02 '11 at 12:40
  • @samar So in a scenario where you have a build system, your software is built by the build server when a developer checks source code in. Your tests are also run after the software is compiled. If someone were to add a new constructor, the test would fail and everyone on the team would be notified that the build was "broken". – Tim Lloyd Feb 02 '11 at 12:53
  • @samar Are you using source control? – Tim Lloyd Feb 02 '11 at 12:53
  • Sorry to have replied so late. Yes, i am using source control. I am using TFS. – samar Feb 03 '11 at 11:08
3

All classes have one constructor. If you don't specify one in the source code, the compiler will add an empty public constructor - the equivalent of:

public class MyClass
{
    public MyClass()
    {
    }
}

However if you specify at least one constructor in the source, only the constructors that you explicitly specify will be created, e.g. the following class has one public constructor that takes a single string parameter:

public class MyClass
{
    public MyClass(string myParameter)
    {
        ...
    }
}

In short, there's nothing special you need to do. If you only want one public constructor then ... just write one public constructor.

Joe
  • 122,218
  • 32
  • 205
  • 338
  • Yes that is true. But in my case my class gets modified by many developers and i want to ensure that they do not write any more constructors of the class than that currently specified. Is it possible? – samar Feb 02 '11 at 10:28
  • Samar, I think it is not possible to restrict other developers who are working on same code to add constructors to the class. – Manoj Attal Feb 02 '11 at 10:42
2

Only the person who codes the class can restrict the number and type of constructors. So if that is you, then you can just code it the way you want.

Peladao
  • 4,036
  • 1
  • 23
  • 43
  • In my case my class gets modified by many developers and i want to ensure that they do not write any more constructors of the class than that currently specified. Is it possible? – samar Feb 02 '11 at 10:28
  • I think his answer is probably still true. If you are not the person then that other person can code it the way they want. – Chris Feb 02 '11 at 10:36
  • @samar Well, for one thing: you can't define restrictions on constructors in the interface. – Peladao Feb 02 '11 at 10:50
1

Constructors are not inherited from base classes.

Your class will have only the constructors that you write, except for (as others have pointed out) a default public constructor that is generated by the compiler when you do not explicitly provide one of your own.

Community
  • 1
  • 1
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • Yes that is true. But in my case my class gets modified by many developers and i want to ensure that they do not write any more constructors of the class than that currently specified. Is it possible? – samar Feb 02 '11 at 10:27
  • @samar: I see. It's not clear from your question that your class is subject to being modified by other developers. And no, as far as I know, there's no built-in way to do that, aside from commenting. If you can't trust your team members any more than that... – Cody Gray - on strike Feb 02 '11 at 10:29
1

This could be achieved using reflection. The only thing you need to take care is, the base class code shouldn't be accessible to or editable by developers.

class Program
{
    static void Main(string[] args)
    {
        Inherited obj = new Inherited("Alpha");
        obj.test();

        Inherited1 obj1 = new Inherited1(); //This will fail as there is no ctor with single param.
        obj1.test();
    }
}

public class MyBase
{
    private static IList<string> ValidatedClasses = new List<string>();

   public MyBase()
   {
      if(!ValidatedClasses.Contains(this.GetType().FullName) && 
          !ValidateConstructorLogic())
      {
          throw new ApplicationException("Expected consturctor with single argument");
      }
   }

    public bool ValidateConstructorLogic()
    {
        bool ValidConstFound = false;

        foreach (var info in this.GetType().GetConstructors())
        {
            if(info.GetParameters().Length ==1)
            {
                lock (ValidatedClasses)
                {
                    ValidatedClasses.Add(this.GetType().FullName);    
                } 

                ValidConstFound = true;
            }

        }

        return ValidConstFound;
    }
}

public class Inherited:MyBase
{
    public Inherited(string test)
    {
        Console.WriteLine("Ctor");
    }

    public void test()
    {
        Console.WriteLine("TEST called");
    }
}

public class Inherited1 : MyBase
{

    public void test()
    {
        Console.WriteLine("TEST called");
    }
}

You could use FxCop to validate your code against a set of predefined rules. I beleive this might be the apt solution to your problem. If you need help on creating custom FxCop rules, please refer this article.

AbrahamJP
  • 3,425
  • 7
  • 30
  • 39
  • 2
    This solution adds a heavy runtime performance penalty to a situation which will never change at runtime. Surely you are better enforcing these constraints outside of the runtime environment. Also, the solution requires that developers not change a different class. Is this not just shifting the problem from one class to another? – Tim Lloyd Feb 02 '11 at 11:46
  • @chibacity: "Is this not just shifting the problem from one class to another?" Maybe but it would be better to maintain 1 class than hundreds of classes. – samar Feb 02 '11 at 11:49
  • 1
    @samar You want to constrain hundreds of classes to have one constructor, and increase the ctor time by a factor of 1000? And only find out about violations at runtime - perhaps after you have released the software? – Tim Lloyd Feb 02 '11 at 11:51
  • Hmm... I agree to what you say. Please do let me know of a better solution than this. – samar Feb 02 '11 at 11:58
  • @Samar Please see my previous answer for an approach that can be used to verify the design constraints through unit testing. This removes runtime checks from the code. – Tim Lloyd Feb 02 '11 at 12:01
  • @Abraham, I am writing this code in wpf. I have added a try -- catch block to it and even after raising the exception you mentioned it is not going in the catch block. I tried changing "ApplicationException" to "Exception" still it is not going in catch. Why is that so? – samar Feb 02 '11 at 12:15
  • 4
    If this solution were used in the real world, it would be a good candidate for "The Daily WTF". And of course, your developers will simply change their classes to derive from a less broken base class, which will defeat your purpose. – Joe Feb 02 '11 at 12:18
  • @Joe, Actually in my application there is only 1 base class from which they can inherit. – samar Feb 02 '11 at 12:29
  • @chibacity: What you said is right. Performance wise this will be an overhead whenever each class is instantiated. I believe we could solve this by caching the classname whose ctor are validated. Thanks for pointing out. – AbrahamJP Feb 02 '11 at 12:56
  • @Abraham Your pattern also introduces the problem of checking and raising exceptions at runtime, perhaps **after** software has been released. In my mind this makes your suggestion dangerous. – Tim Lloyd Feb 02 '11 at 12:59
  • @Abraham You could perhaps try to mitigate performance issues by caching results, but you would have to make this thread-safe which opens up another can of worms in terms of complexity, but also different performance issues. – Tim Lloyd Feb 02 '11 at 13:01
  • @Abraham Your updated code is not thread-safe on a number of points. For example, the collection is accessed outside of a lock and it is possible to add duplicates to the list. Even if it were thread-safe, you have introduced a different performance issue - lock contention. Your example is now even more dangerous as it stands, as it can throw exceptions related to threading issues as well as exceptions for classes which do not meet ctor requirements. – Tim Lloyd Feb 02 '11 at 13:14
  • It seems like FxCop(custom rules) might be able to sort out this. – AbrahamJP Feb 02 '11 at 13:34
  • @Abraham Your current code will also pass a class that has multiple constructors with single parameters and add multiple entries for the same class to your validation list. Your validation is incorrect. – Tim Lloyd Feb 02 '11 at 13:35
  • @Abraham Your current validation code would pass a class which had a constructor with a single parameter, but also other constructors, as long as the constructor with a single parameter was encountered first. – Tim Lloyd Feb 02 '11 at 13:37
  • @chibacity: What I provided was a basic skeleton. – AbrahamJP Feb 02 '11 at 13:44
  • @Abraham What you have provided is incorrect and has many issues, including threading issues. Even if your intention is to provide a skeleton, it should still be correct. – Tim Lloyd Feb 02 '11 at 13:46
  • @chibacity: If you have a better solution than the FxCop custom rules or the code snippet provided, please share with us. – AbrahamJP Feb 02 '11 at 13:51
  • @Abraham I provided my own answer 3 hours ago. If you want to provide your FxCop solution as an answer, please add another answer. It's not an answer if you only mention it in a comment. – Tim Lloyd Feb 02 '11 at 13:52
  • @chibacity: ok thanks for mentioning that. I will be adding that to my answer. – AbrahamJP Feb 02 '11 at 14:23
  • @Abraham Please do consider that the answer is an answer to the general question. The OPs circumstances are not necessarily relevant re. not having a build system. For example, the build system situation is not included in the question, also someone else that **does** have a build system has the right to a decent answer for this question. You do not need a build system to run tests, you need to write unit tests. Having a build system is best practice. If the OP has a large dev team and no build system or unit tests, then he has bigger problems to deal with. – Tim Lloyd Feb 02 '11 at 14:29
  • @Abraham Again, if you feel FxCop is an answer, please add it as an answer and stop commenting about it. Lets see some detail. Will it work as a decent solution without a build system? – Tim Lloyd Feb 02 '11 at 14:31
0

You could try using a nested builder, as described by Jon Skeet. Basically: You force the user to go through the builder which then calls the private class constructor. Since the class constructor is private, only the nested builder has access to it.

Alternative: Use static factory methods, make the constructor private & document your intentions.

Community
  • 1
  • 1
Mark Simpson
  • 23,245
  • 2
  • 44
  • 44
0

Based on your comments, I don't think this is a "coding" problem. This is a policy & enforcement problem. You don't want other developers in your team creating more constructors.

In that case, go tell them that. Whoever is in charge of your source code repository can enforce it by rejecting changes that break the policy. Adding code to deal with this is just going to add runtime penalties to users for no reason.

Tridus
  • 5,021
  • 1
  • 19
  • 19