How to call one constructor from another in C# to avoid code duplication?
I have a C# class with two constructors that initialize 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 values directly, while the other performs calculations before setting the fields. I need to:
- Avoid duplicating the field initialization code
- Keep fields readonly (they must be set in the constructor)
- Call one constructor from another
What’s the proper way to implement constructor chaining in C# to share initialization logic between constructors?
Constructor chaining in C# allows you to call one constructor from another using the this() syntax, eliminating code duplication while maintaining readonly field initialization. The key is to have the more complex constructor call the simpler one after performing necessary calculations, ensuring all readonly fields are properly set during object construction.
Contents
- Understanding Constructor Chaining
- The
this()Syntax - Implementing Constructor Chaining with Readonly Fields
- Best Practices and Patterns
- Primary Constructors in Modern C#
- Common Use Cases
Understanding Constructor Chaining
Constructor chaining is the process of calling one constructor from another constructor within the same class or from a base class. As NashTech Blog explains, “Constructor Chaining is the process of calling one constructor from another constructor within the same class or from a base class.”
This technique serves several important purposes:
- Code reusability: Eliminates duplicate initialization code across constructors
- Consistency: Ensures all constructors follow the same initialization pattern
- Maintainability: Changes to initialization logic only need to be made in one place
- Readonly field management: Properly handles readonly field initialization requirements
The order of execution is crucial: “First, we call the constructor which is referenced with the :this() keyword, and if that also references another constructor, it will also call that constructor, climbing up the call chain” according to Pluralsight.
The this() Syntax
The this() syntax is used to call one constructor from another constructor in the same class. The syntax places the call at the beginning of the constructor definition, followed by a colon and the this() keyword with appropriate parameters.
public ClassName(parameters)
: this(otherParameters)
{
// Constructor body executes after the chained constructor completes
}
Key rules about constructor chaining:
- The
this()call must be the first statement in the constructor - Only one constructor can be called (either
this()orbase()) - You can chain multiple constructors through a chain of calls
- The call chain must eventually reach a constructor that doesn’t call another
As Tutorial.TechAltum states, “This can be done by the keyword this and base.”
Implementing Constructor Chaining with Readonly Fields
For your specific example with readonly fields, the proper implementation would be:
public class Sample
{
public Sample(string theIntAsString)
: this(int.Parse(theIntAsString))
{
// Additional string-specific initialization if needed
}
public Sample(int theInt) => _intField = theInt;
public int IntProperty => _intField;
private readonly int _intField;
}
In this implementation:
- The string constructor calls the int constructor using
: this(int.Parse(theIntAsString)) - The int constructor performs the actual field assignment
- Both constructors properly initialize the readonly field
_intField - No code duplication exists between constructors
The Microsoft Learn documentation confirms that “readonly members can only assigned in the class level or on its constructor,” which is satisfied by this approach.
You could also implement it the other way around, having the more specific constructor call the more general one:
public class Sample
{
public Sample(int theInt)
{
_intField = theInt;
}
public Sample(string theIntAsString)
: this(int.Parse(theIntAsString))
{
// String-specific logic here if needed
}
public int IntProperty => _intField;
private readonly int _intField;
}
Both approaches are valid, but the first one is generally preferred when one constructor is clearly more fundamental than the other.
Advanced Example with Multiple Readonly Fields
For more complex scenarios with multiple readonly fields:
public class ComplexSample
{
public ComplexSample(string data)
: this(ParseData(data))
{
// Additional string-specific processing
}
public ComplexSample(ParsedData parsedData)
{
_primaryField = parsedData.Value;
_secondaryField = CalculateSecondary(parsedData);
_timestampField = DateTime.UtcNow;
}
// Properties
public int PrimaryProperty => _primaryField;
public string SecondaryProperty => _secondaryField;
public DateTime TimestampProperty => _timestampField;
private readonly int _primaryField;
private readonly string _secondaryField;
private readonly DateTime _timestampField;
private static ParsedData ParseData(string data)
{
// Parsing logic
return new ParsedData { Value = int.Parse(data) };
}
private static string CalculateSecondary(ParsedData data)
{
// Calculation logic
return $"Value: {data.Value}";
}
}
public class ParsedData
{
public int Value { get; set; }
}
Best Practices and Patterns
When implementing constructor chaining, consider these best practices:
1. Chain to the most general constructor
- Have more specific constructors call more general ones
- This creates a clear hierarchy of initialization logic
2. Handle validation early
- Perform parameter validation in the constructor that receives the parameters
- Chain to the next constructor only after validation passes
public class ValidatedSample
{
public ValidatedSample(int value)
: this(value, ValidateValue(value))
{
}
public ValidatedSample(int value, string validationMessage)
{
_value = value;
_validationMessage = validationMessage;
}
private static string ValidateValue(int value)
{
if (value < 0)
throw new ArgumentException("Value cannot be negative");
return "Valid";
}
private readonly int _value;
private readonly string _validationMessage;
}
3. Use private constructors for shared initialization
- As suggested on Stack Overflow, “you can make a private constructor that can accept both a types of argument, and have your two original constructors both simply chain this one, passing null for the missing argument. This has the advantage over calling private initialisation methods that it plays nicely with readonly fields”
4. Consider parameter object pattern for complex constructors
- When constructors have many parameters, consider using a parameter object
public class ParameterizedSample
{
public ParameterizedSample(string data)
: this(new SampleParameters(data))
{
}
public ParameterizedSample(SampleParameters parameters)
{
_field1 = parameters.Value1;
_field2 = parameters.Value2;
}
private readonly int _field1;
private readonly string _field2;
}
public class SampleParameters
{
public SampleParameters(string data)
{
Value1 = int.Parse(data);
Value2 = data.ToUpper();
}
public int Value1 { get; }
public string Value2 { get; }
}
Primary Constructors in Modern C#
C# 12 introduced primary constructors, which provide a more concise syntax for constructor chaining. As Microsoft Learn explains, “Every other constructor for a class must call the primary constructor, directly or indirectly, through a this() constructor invocation.”
Here’s how your example would look with primary constructors:
public class Sample(int intField)
{
public Sample(string theIntAsString)
: this(int.Parse(theIntAsString))
{
}
public int IntProperty => intField;
}
Key benefits of primary constructors:
- More concise syntax
- Automatic creation of field/parameter
- Enforced initialization through chaining
- Better integration with other language features
However, primary constructors have some limitations:
- All parameters are publicly accessible by default
- Parameter validation can be less obvious
- May not be suitable for all scenarios
Common Use Cases
Constructor chaining is particularly useful in these scenarios:
1. Optional parameters and default values
public class OptionalParameterSample
{
public OptionalParameterSample(string name, int age = 18)
{
_name = name;
_age = age;
}
public OptionalParameterSample(string name)
: this(name, 18) // Default age
{
}
private readonly string _name;
private readonly int _age;
}
2. Factory pattern implementations
public class FactorySample
{
private FactorySample() { }
public static FactorySample CreateFromData(string data)
=> new FactorySample { _data = data };
public static FactorySample CreateFromFile(string filePath)
=> CreateFromData(File.ReadAllText(filePath));
private string _data;
}
3. Builder pattern integration
public class BuilderSample
{
public BuilderSample(Builder builder)
{
_field1 = builder.Field1;
_field2 = builder.Field2;
}
public static Builder CreateBuilder() => new Builder();
private readonly string _field1;
private readonly int _field2;
public class Builder
{
public string Field1 { get; set; }
public int Field2 { get; set; }
public BuilderSample Build() => new BuilderSample(this);
}
}
4. Exception handling scenarios
public class ExceptionHandlingSample
{
public ExceptionHandlingSample(string data)
: this(data, ValidateAndParse(data))
{
}
public ExceptionHandlingSample(string data, int parsedValue)
{
_data = data;
_value = parsedValue;
}
private static int ValidateAndParse(string data)
{
try
{
return int.Parse(data);
}
catch (FormatException ex)
{
throw new ArgumentException("Invalid data format", nameof(data), ex);
}
}
private readonly string _data;
private readonly int _value;
}
Conclusion
Constructor chaining using the this() syntax is the proper way to share initialization logic between constructors in C# while maintaining readonly field requirements. Key takeaways:
- Use
: this(parameters)syntax to call one constructor from another, ensuring it’s the first statement - Chain to the most general constructor to create a clear initialization hierarchy
- Handle readonly fields properly by assigning them only in constructors
- Consider primary constructors in modern C# for more concise syntax
- Apply validation and error handling in the appropriate constructor in the chain
By implementing constructor chaining correctly, you eliminate code duplication, improve maintainability, and ensure consistent object initialization across all constructor overloads.
Sources
- How to do constructor chaining in C# - Stack Overflow
- Call Chain of Constructors in C# | Pluralsight
- C# - Call a constructor from another constructor | makolyte
- Understanding Constructor Chaining in C# - NashTech Blog
- Constructor Chaining in C# - Tutorial.TechAltum
- Constructor Chaining : C# 411
- Constructor Chaining – 2,000 Things You Should Know About C#
- Declare C# primary constructors – classes, structs | Microsoft Learn
- readonly keyword - C# reference | Microsoft Learn
- C# Constructors with Examples - Tutlane