Avi
Avi

Reputation: 16176

Call one constructor from another

I have two constructors which feed values to readonly fields.

public class Sample
{
    public Sample(string theIntAsString)
    {
        int i = int.Parse(theIntAsString);
        _intField = i;
    }

    public Sample(int theInt) => _intField = theInt;
    public int IntProperty    => _intField;

    private readonly int _intField;
}

One constructor receives the values directly, and the other does some calculation and obtains the values, then sets the fields.

Now here's the catch:

  1. I don't want to duplicate the setting code. In this case, just one field is set but of course there may well be more than one.
  2. To make the fields readonly, I need to set them from the constructor, so I can't "extract" the shared code to a utility function.
  3. I don't know how to call one constructor from another.

Any ideas?

Upvotes: 1348

Views: 698327

Answers (11)

Zoyeb Shaikh
Zoyeb Shaikh

Reputation: 334

using System;
using System.Linq;
using System.Text;
using System.Threading;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Customer customer = new Customer();
            customer.PrintFullName();

            Customer customer1 = new Customer("Zoyeb", "Shaikh");
            customer1.PrintFullName();
        }

    }

    public class Customer
    {
        string _firstName;
        string _lastName;

        public Customer(string fn, string ln)
        {
            _firstName = fn;
            _lastName = ln;
        }

        public Customer() : this("No-First Name","No-Last Name")
        {

        }
        public void PrintFullName()
        {
            Console.WriteLine("Full Name = {0}", this._firstName + " " + this._lastName);
        }
    }

}

Upvotes: 1

Mark Ainsworth
Mark Ainsworth

Reputation: 855

In my case, I had a main constructor that used an OracleDataReader as an argument, but I wanted to use different query to create the instance:

I had this code:

public Subscriber(OracleDataReader contractReader)
    {
        this.contract = Convert.ToString(contractReader["contract"]);
        this.customerGroup = Convert.ToString(contractReader["customerGroup"]);
        this.subGroup = Convert.ToString(contractReader["customerSubGroup"]);
        this.pricingPlan= Convert.ToString(contractReader["pricingPlan"]);
        this.items = new Dictionary<string, Member>();
        this.status = 0;
        
        
    }

So I created the following constructor:

public Subscriber(string contract, string customerGroup) : this(getSubReader(contract, customerGroup))
    { }

and this method:

 private static  OracleDataReader getSubReader(string contract, string customerGroup)
    { 
        cmdSubscriber.Parameters[":contract"].Value = contract + "%";
        cmdSubscriber.Parameters[":customerGroup"].Value = customerGroup+ "%";
        return  cmdSubscriber.ExecuteReader();
        
    }

notes: a statically defined cmdSubscriber is defined elsewhere in the code; My main constructor has been simplified for this illustration.

Upvotes: 1

Kasper Jensen
Kasper Jensen

Reputation: 622

Error handling and making your code reusable is key. I added string to int validation and it is possible to add other types if needed. Solving this problem with a more reusable solution could be this:

public class Sample
{
    public Sample(object inputToInt)
    {
        _intField = objectToInt(inputToInt);
    }

    public int IntProperty => _intField;

    private readonly int _intField;
}

public static int objectToInt(object inputToInt)
{
    switch (inputToInt)
        {
            case int inputInt:
                return inputInt;
            break;
            case string inputString:
            if (!int.TryParse(inputString, out int parsedInt))
            {
                throw new InvalidParameterException($"The input {inputString} could not be parsed to int");
            }
            return parsedInt;

            default:
                throw new InvalidParameterException($"Constructor do not support {inputToInt.GetType().Name}");
            break;
        }
}

Upvotes: 2

supercat
supercat

Reputation: 81247

If what you want can't be achieved satisfactorily without having the initialization in its own method (e.g. because you want to do too much before the initialization code, or wrap it in a try-finally, or whatever) you can have any or all constructors pass the readonly variables by reference to an initialization routine, which will then be able to manipulate them at will.

public class Sample
{
    private readonly int _intField;
    public int IntProperty => _intField; 

    private void setupStuff(ref int intField, int newValue) => intField = newValue;

    public Sample(string theIntAsString)
    {
        int i = int.Parse(theIntAsString);
        setupStuff(ref _intField,i);
    }

    public Sample(int theInt) => setupStuff(ref _intField, theInt);
}

Upvotes: 206

SLaks
SLaks

Reputation: 887957

Like this:

public Sample(string str) : this(int.Parse(str)) { }

Upvotes: 2127

SUNIL DHAPPADHULE
SUNIL DHAPPADHULE

Reputation: 2873

Constructor chaining i.e you can use "Base" for Is a relationship and "This" you can use for same class, when you want call multiple Constructor in single call.

  class BaseClass
{
    public BaseClass():this(10)
    {
    }
    public BaseClass(int val)
    {
    }
}
    class Program
    {
        static void Main(string[] args)
        {
            new BaseClass();
            ReadLine();
        }
    }

Upvotes: 8

Lineesh K Mohan
Lineesh K Mohan

Reputation: 1712

When you inherit a class from a base class, you can invoke the base class constructor by instantiating the derived class

class sample
{
    public int x;

    public sample(int value)
    {
        x = value;
    }
}

class der : sample
{
    public int a;
    public int b;

    public der(int value1,int value2) : base(50)
    {
        a = value1;
        b = value2;
    }
}

class run 
{
    public static void Main(string[] args)
    {
        der obj = new der(10,20);

        System.Console.WriteLine(obj.x);
        System.Console.WriteLine(obj.a);
        System.Console.WriteLine(obj.b);
    }
}

Output of the sample program is

50 10 20


You can also use this keyword to invoke a constructor from another constructor

class sample
{
    public int x;

    public sample(int value) 
    {
        x = value;
    }

    public sample(sample obj) : this(obj.x) 
    {
    }
}

class run
{
    public static void Main(string[] args) 
    {
        sample s = new sample(20);
        sample ss = new sample(s);

        System.Console.WriteLine(ss.x);
    }
}

The output of this sample program is

20

Upvotes: 5

Yeah, you can call other method before of the call base or this!

public class MyException : Exception
{
    public MyException(int number) : base(ConvertToString(number)) 
    {
    }

    private static string ConvertToString(int number) 
    { 
      return number.toString()
    }

}

Upvotes: 9

Sudantha
Sudantha

Reputation: 16214

Before the body of the constructor, use either:

: base (parameters)

: this (parameters)

Example:

public class People: User
{
   public People (int EmpID) : base (EmpID)
   {
      // Add more statements here.
   }
}

Upvotes: 81

pasx
pasx

Reputation: 2985

Here is an example that calls another constructor, then checks on the property it has set.

    public SomeClass(int i)
    {
        I = i;
    }

    public SomeClass(SomeOtherClass soc)
        : this(soc.J)
    {
        if (I==0)
        {
            I = DoSomethingHere();
        }
    }

Upvotes: 11

Faisal Mq
Faisal Mq

Reputation: 5554

I am improving upon supercat's answer. I guess the following can also be done:

class Sample
{
    private readonly int _intField;
    public int IntProperty
    {
        get { return _intField; }
    }

    void setupStuff(ref int intField, int newValue)
    {
        //Do some stuff here based upon the necessary initialized variables.
        intField = newValue;
    }

    public Sample(string theIntAsString, bool? doStuff = true)
    {
        //Initialization of some necessary variables.
        //==========================================
        int i = int.Parse(theIntAsString);
        // ................
        // .......................
        //==========================================

        if (!doStuff.HasValue || doStuff.Value == true)
           setupStuff(ref _intField,i);
    }

    public Sample(int theInt): this(theInt, false) //"false" param to avoid setupStuff() being called two times
    {
        setupStuff(ref _intField, theInt);
    }
}

Upvotes: 16

Related Questions