Constructor chaining or use named/optional parameters
Asked Answered
C

4

6

I'm a very new programmer and understand we want to minimize code redundancy so when we update we can make as few changes as possible and also minimize errors.

So I have a Student class that I want overloaded constructor so If I do back chaining it's like this.

 public class Student
{
    string name;
    int Id;

    public Student()
    {
    }
    public Student(string name)
    {
        this.name = name;
    }
    public Student(string name, int Id) :this(name)
    {
        this.Id = Id;

But this is apparently bad because we want all the code in one constructor for what reason? Ease of reading?

    public Student() : this(null, 0)
    { }
    public Student(string name) : this(name, 0)
    { }
    public Student(string name, int Id) 
    {
        this.name = name;
        this.Id = Id;
    }

If I do this forward chaining then what happens if we add a new field like string major? If I do forward chaining then I create a new overloaded constructor with the added field. But now I have to change all the other constructors to call the new one. If I do backwards chaining I just create a new constructor and call the previous overloaded one.

    public Student(string name, int Id, string major):this(name, Id)
    {
        this.major=major;
    } 

This seems like it follows OOP better but all examples in my text book show forward chaining and don't say why I shouldn't use back chaining.

If I used named/optional parameters it's even easier.

    public Student(string name = null, int Id = 0, string major = null)
    {
        this.name = name;
        this.Id = Id;
        this.major = major;
    }

And if I need another field I just have to edit the one and only constructor. This seems to follow OOP principles the best or am I mistaken? It at least eliminates code duplication the most. My assignment was to "implement the student class following the principles of OOP". I know all these are valid but is one way the best/accepted way of coding constructors? Are there shortcomings with named/optional params that I'm missing? As a beginner it's very confusing that there's so many ways to code this.

Cannular answered 2/6, 2016 at 3:31 Comment(2)
Chained constructors were extensively used on C# before optionals, optionals were added on C# 3.0, so a lot of info you can read still use them because is old info, but today, with optional parameters, chained constructors have lost their meaning if on the chain you just pass default values.Hibernate
What Gusman said. Also, you can forward chain one step at a time: public Student() : this(null) {} instead of public Student() : this(null, 0) {}. (Although this can become ambiguous if you have two constructors that take a single nullable argument). That way, you only need to change the last c'tor in the chain when you add a new field.Alleenallegation
T
3

There is no best way, because they are different, each of them has advantages and disadvantages.

Independent constructors and chained constructors are available since C# 1.0, under most circumstances we use chained constructors but sometimes we have to use the former if the two constructors has totally different things to handle.

class Student
{
    public Student()
    {
        DoSomething();
    }

    public Student(string name)
    {
        this.Name = name;
        DoAnotherThing();
    }
}

Comparing with optional parameters constructor, the above two are much longer, but to be honest, they are much safer. Consider the following case:

public class Student
{
    public Student(string firstName = null, string lastName = null)
    {
        this.FirstName = firstName;
        this.LastName = lastName;
    }

    public string FirstName { get; set; }
    public string LastName { get; set; }
}

//it's working fine
//var danny = new Student("danny", "chen");

//as the business grows a lot of students need a middle name
public Student(string firstName = null, string middleName = null, string lastName = null)
{
    this.FirstName = firstName;
    this.MiddleName = middleName;
    this.LastName = lastName;
}

//for a better sequence the programmer adds middleName between the existing two, bang!

Another difference between them is using reflection. Optional parameters will not generate more constructors for you, and the default values will not be applied if you are invoking the constructor using reflection.

Thayne answered 2/6, 2016 at 5:59 Comment(0)
P
1

I know all these are valid but is one way the best/accepted way of coding constructors?

Not to the best of my knowledge - you have many options. Your colleagues are the best people to talk to to reach a consensus. C# is becoming a very rich language, and that means that there will be many different ways to achieve the same thing.

Are there shortcomings with named/optional params that I'm missing?

Not to the best of my knowledge. Personally I think this is the best solution, but others may differ.

As a beginner it's very confusing that there's so many ways to code this.

Welcome to programming. There's millions of us programmers, and we're all busy adding complexity to the world!

Pistachio answered 2/6, 2016 at 3:40 Comment(0)
D
1

I suggest looking deeper about this assumption :

"But this is apparently bad because we want all the code in one constructor for what reason"

There is Fluent Interface Pattern that puts these things apart, also I can not strongly argue that this directly translates for constructors.

customer
    .NameOfCustomer("Shiv")
    .Bornon("12/3/1075")
    .StaysAt("Mumbai");

Illustrative implementation :

public class Customer
{
    public string FullName { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string Address { get; set; }

    public Customer NameOfCustomer(string name)
    {
        this.FullName = name;
        return this;
    }

    public Customer BornOn(DateTime dateOfBirth)
    {
        this.DateOfBirth = dateOfBirth;
        return this;
    }

    public Customer StaysAt(string address)
    {
        this.Address = address;
        return this;
    }
} 

Example source

Divinize answered 2/6, 2016 at 5:24 Comment(0)
M
0

This is a partial answer. Your "backwards chaining", with one constructor setting some parameter(s) then calling another constructor for the rest, is very reasonable/sensible for normal functions: one function does part of the work, then calls another function for some other work, and so on.

But for public constructors, any one of them can be called by client code, expecting to get a fully constructed object, fully initialized. Some constructors in your example leave members uninitialized (unless what you want is the default value for the type).

As an alternative, you can have private constructors.

Migratory answered 30/12, 2019 at 12:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.