9

I find that in some cases, there is a lot of code in a constructor, or a class has two or more constructors which have comparable code. In these cases, I often create a private method. In the former case to improve readability, in the latter to prevent duplicate code.

In some of these cases this results in a private method that should ONLY be called from the constructor (for whatever reason). Is there a way to enforce this? I could imagine doing something like this:

using System.Diagnostics;

public class Foo
{
  private bool _constructing = true;

  private Foo()
  {
    _constructing = false;
  }

  public Foo(string someString) : this()
  {
    // constructor-specific code
    Initialize();
  }

  public Foo(double someDouble) : this()
  {
    // constructor-specific code
    Initialize();
  }

  private void Initialize()
  {
    Debug.Assert(!_constructing, "Initialize method should only be called from constructor");

    // shared code
  }
}

but this feels somewhat clunky. Does anyone have a better suggestion?

Edit: added constructor chaining to example; I meant for this to be in the original example.

Edit: I think I missed a point in my original question - while chaining constructors does provide a solution in some cases, the chained code is always executed prior to the code in the constructor that you're chaining from (which, incidentally, is why the above example doesn't work). There are cases where you want to execute some part of shared code, and then do something else. I'll add another example to reflect this:

using System.Diagnostics;

public class Foo
{
  private bool _constructing = true;

  public Foo(string someString)
  {
    // constructor-specific pre-processing code
    Initialize();
    // constructor-specific post-processing code

    _constructing = false;
  }

  public Foo(double someDouble)
  {
    // constructor-specific pre-processing code
    Initialize();
    // constructor-specific post-processing code

    _constructing = false;
  }

  private void Initialize()
  {
    Debug.Assert(!_constructing, "Initialize method should only be called from constructor");

    // shared code
  }
}
MHJF
  • 127
  • 8
  • 5
    You can chain constructors, which removes the need for a separate method. This assumes that there is similar stuff happening in different constructors... http://stackoverflow.com/questions/1814953/c-sharp-constructor-chaining-how-to-do-it – David Kirkland Jan 14 '15 at 10:10
  • Mark `_constructing` as `readonly`. This way you'll be sure no other code will be able to revert it to `true`. – Lucas Trzesniewski Jan 14 '15 at 10:11
  • 4
    Since the method is private, I cannot see why you would need to do anything other than put a comment next to the method saying that it should only be called from the constructor. – Matthew Watson Jan 14 '15 at 10:16
  • Use constructor chaining. That `Initialize` method isn't useful if you need to initialize a `readonly` field. – Sriram Sakthivel Jan 14 '15 at 10:19
  • I actually meant to use constructor chaining in the example; forgot to double-check my own example code - sorry! – MHJF Jan 14 '15 at 10:27
  • Your last example looks rather complicated. Perhaps your should look for other solutions, such as hierarchy instead of single class, or pattern Builder. – Mark Shevchenko Jan 14 '15 at 11:00
  • @MarkShevchenko That was exactly why I'm looking for a more elegant solution - and yes, it may be that I need to use a different method altogether. Please note that this is particularly important when working as part of a larger group, where there is a risk of (accidental) abuse of these private methods. – MHJF Jan 15 '15 at 07:29

3 Answers3

9

Constructors can call each other:

class Foo
{
    private Foo()
    {

    }

    public Foo(int value) : this()
    {

    }
}

I think, you could use this feature.

Mark Shevchenko
  • 7,937
  • 1
  • 25
  • 29
  • Oops - that's actually what I meant to do in my example - not exactly what I was looking for though. I'll edit the example – MHJF Jan 14 '15 at 10:25
  • After your editing: you need to turn your `Initialize` method in yet another constructor (private with unique parameters) and then call it. In your case it seems it's best to turn it in parameter-less private constructor. – Mark Shevchenko Jan 14 '15 at 10:32
5

You can use the CallerMemberName for that. The compiler will fill that with the original method the method is called from. In this case .ctor (constructor):

public static class Program
{
    static void Main(string[] args)
    {
        A a = new A();
    }
}

class A
{
    public A()
    {
        B();
    }

    [MethodImplOptions.NoInlining]
    private void B([CallerMemberName] string caller = null)
    {
        if (caller == ".ctor")
        {
        }
    }
}

To prevent inlining, you can put the MethodImplOptions.NoInlining on the method B.

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
  • 1
    You can still call `B(".ctor");`, but at least this causes a warning. – Rawling Jan 14 '15 at 10:16
  • 3
    But can't I still call `B(".ctor")` in that case? AFAIK `CallerMemberName` is only filled if the optional parameter is not supplied... – Christoph Fink Jan 14 '15 at 10:16
  • I just tried using CallerMemberName (it seems to be what I'm looking for) but unfortunately, I'm stuck to using .NET 4.0 for the moment... This seems to have been implemented in 4.5, right? – MHJF Jan 14 '15 at 10:37
  • @MHJFaase: Check this out: http://www.journeyintocode.com/2013/04/callermembername-net-40.html – Patrick Hofman Jan 14 '15 at 10:40
  • I'm going to accept this as the solution - although I can't actually use this way it does seem to be closest to what I want - I'll either have to start using 4.5 or just grit my teeth for a while... – MHJF Jan 15 '15 at 07:26
4

I suggest that you do this:

public class Foo
{
    private Foo()
    {
        // private constructors can only be called from
        // within the class during constuction
    }

    public Foo(string someString) : this()
    {
    }

    public Foo(double someDouble) : this()
    {
    }
}

The use of : this() can only be called during construction and while this doesn't force your public constructors calling : this() you don't have any such guarantee that your public constructors will call Initialize() anyway.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • There is a situation that is particularly relevant here : serialisation. I have often wanted an Initialise() function in a serialisable class that can be called by both the constructor and by a deserialiser, both of which have the job of initailsing the contents of the instance. It would be really tidy to be able to specify that this Initialise() function was allowed to modify readonly fields. – Dave Feb 01 '18 at 14:00