2

The following pattern is common in my code: A class has several constructors. One is the "designated constructor", the others are for convenience. The code may look like this:

class Foo 
{
  Foo(int bar, string baz) 
  {
    this.Bar = bar;
    this.Baz = baz;
  }

  Foo()
    : this(0, "Empty baz")
  {
  }

  Foo(Foo f)
    : this(f.Bar, f.Baz)
  {
  }

  int Bar {get;set;}
  string Baz {get;set;}
}

In the case of the parameterless constructor calling this(...), this works fine. However, if someone passes null as an argument to the copy constructor Foo(Foo), the result will be an null reference exception because of the expression f.Bar. Because I would like to see an ArgumentNullException instead, I usually stray from the pattern in cases like this and implement the constructor "manually", which results in duplicate code. Is there an elegant way to avoid this, i.e. having one designated constructor and still perform parameter validation for the others?

Edited: This example is bare-bones just to describe the issue. In real-world examples, there will be more complex argument validation logic and initialization code.

3
  • You could do in-line null checking before attempting to propagate the property values, but this doesn't help validation and is not elegant. Commented Jul 17, 2012 at 10:32
  • 1
    Maybe you don't know this, but you can use the following c# syntax: new Foo() { Bar = 1, Baz = "test" };. I've found that it makes those kinds of constructors useless (unless you need required properties of course) Commented Jul 17, 2012 at 10:38
  • @Team-JoKi That's useful syntax; my example is about constructors with required parameters, as you have guessed (in the example I put "get;set;" to abbreviate the code) Commented Jul 17, 2012 at 11:31

3 Answers 3

2

You could check this as follows:

class Foo
{
    private static Foo ThrowIfNull(Foo foo)
    {
        if (foo == null) throw new ArgumentNullException("foo");
        return foo;
    }

    ...

    Foo(Foo f) : this (ThrowIfNull(f).Bar, f.Baz)
    {
    }
}

It'll throw your stack trace off a little, but not too much.

Sign up to request clarification or add additional context in comments.

1 Comment

+1 Quite cute, that's a dangerous candidate for object extension methods lol
1

You should always check parameters you get from outside your class-scope.

Comments

0

Are you sure this code could not be converted to: (?)

  Foo(Foo f)
  {
       if(f != null) 
       {
            Bar = f.Bar;
            Baz = f.Baz;
       }
       else
       {
           throw new ArgumentNullException("f");
       }
  }

Honestly I believe this is the way to go in your case. Why overcomplicating things?

You're looking to set some properties during construction-time, and you can implement this in the constructor itself. Because you want to avoid 1, 2 or 3 lines, you're trying to invent a wheel! ;)

OPTION B ··· UPDATE!!

There's another approach. What about a single constructor covering your use cases but using optional parameters?

Your constructor would be like this:

Foo(int bar = -1, string baz = null, Foo f = null) 
  {
    if(bar >= 0 && !string.IsNullOrEmpty(baz)) 
    {
       Bar = bar;
       Baz = baz;
    }
    else if(f != null) 
    {
       Bar = f.Bar;
       Baz = f.Baz;
    }
    else
    {
        Bar = 0;
        Baz = "Empty baz";
    }
  }

Depending on how you invoke this constructor, it's going to do a thing or other.

3 Comments

I think that this was just a simple example. The main point was that he wants to have one "main" constructor which does the actual instantiation, and then have all other convenience constructors delegate instantiation to the main constructor. For the simple case of just assigning a variable, copying the assignments from the main constructor (like you did) might be fine, but if the main constructor contains complex logic and there are many convenience constructors, this will result in code copied into multiple locations, which is generally a bad thing.
I'm agreed, but we'd see actual use case samples in order to determine the best solution. Sometimes will be fine with my answer, others no.
@Alderath You're spot on, the real benefit is gained when the constructor contains serious construction logic.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.