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:
- 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.
- 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.
- I don't know how to call one constructor from another.
Any ideas?
11 Answers 11
Like this:
public Sample(string str) : this(int.Parse(str)) { }
14 Comments
static
method that manipulates the parameters.Sample(string)
will be executed first then Sample(int)
or the int version will be executed first then it will get back to the string version? (Like calling super()
in Java?)this
until its base class has been initialized.?:
or call a static method.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);
}
12 Comments
base(...)
or this(...)
we can only perform very limited operations.out
keyword instead of ref
?out
in the example might not make that clear. Also, I'm generally a bit skeptical of out
, since there's no guarantee that an outside method with an out
parameter will actually store anything there.readonly
.readonly
field needs to be set, the code which sets it could call the method and assign the field using the return value, but any number of fields may be written directly with ref
. Also, in case it matters, changes made via the ref
parameter take place immediately, even before the function returns, while those made using a function return value would not.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.
}
}
4 Comments
init()
. You can call this method from any of your constructors.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);
}
}
4 Comments
new Sample(str, false)
.this
, then let that constructor call setupStuff
; remove the call to setupStuff in the last constructor. Then you don't need the doStuff
/ false
parameter. (A lesser complaint is that if you do have a reason to use doStuff
parameter, there is no benefit to making that a nullable Boolean bool?
. Just use bool
.) Also, what Teejay pointed out, means this is a fatally flawed design.public Sample(string theIntAsString) : this(int.Parse(theIntAsString)) {}
public Sample(int theInt) { setupStuff(ref _intField, theInt); }
Note that the first constructor, which calls another constructor, does not call setupStuff
.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();
}
}
1 Comment
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()
}
}
1 Comment
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();
}
}
Comments
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
Comments
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;
}
}
1 Comment
objectToInt
won't help then.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.
Comments
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);
}
}
}