C# Primary Constructor (How It Works For Developer)

In the Object-oriented landscape of C# programming, the introduction of Primary Constructors brings a new level of elegance and simplicity to the language. Primary constructors, alongside features such as intercepters and collection expressions, emerged in C# 12 as a powerful feature, offering a more concise syntax for declaring constructors with parameters. You can explore Primary constructors in depth on the Microsoft C# guide.

In this article, we'll learn how to use C# 12 Primary Constructors efficiently, also exploring their functionality, use cases, and how they transform the way developers approach class initialization.

Understanding the Basics: Constructors in C#

Constructors play a pivotal role in object-oriented programming, serving as the blueprint for initializing objects. Traditionally, C# developers have used the default constructor or parameterized constructors to set up the initial state of their classes. However, the introduction of Primary Constructors adds a more streamlined approach to this essential aspect of C# development.

The Essence of Primary Constructors

A Primary Constructor in C# is a concise way to declare and initialize properties directly within the class declaration. It simplifies the process of defining and assigning values to properties, offering a more declarative and readable syntax.

Benefits of Primary Constructors

  1. Conciseness: Primary constructors provide a succinct syntax, reducing boilerplate code and enhancing readability.
  2. Scoping: Unlike traditional constructors, parameters in primary constructors are in scope throughout the entire class or struct, offering flexibility in their usage.
  3. Default Values: Default parameter values simplify object creation, making it more convenient for developers.

Declaring a Primary Constructor

The syntax for a Primary Constructor involves declaring the properties directly in the class header. Let's consider a basic Person class example:

public class Person(string name, int age)
{
    public string Name { get; } = name;
    public int Age { get; } = age
    public override string ToString() => $"Name: {Name}, Age: {Age}";
}
public class Person(string name, int age)
{
    public string Name { get; } = name;
    public int Age { get; } = age
    public override string ToString() => $"Name: {Name}, Age: {Age}";
}
Public Class Person(String name, Integer age)
	Public ReadOnly Property Name() As String = name
	public Integer Age {get;} = age public override String ToString()
	If True Then
		Return $"Name: {Name}, Age: {Age}"
	End If
End Class
VB   C#

In the above code snippet, the Person class has a Primary Constructor that initializes the instance member Name and instance member Age properties. The constructor parameters are declared with the class or struct name and at the time of defining public properties the parameter values are assigned to them.

Example 1: Immutable Point in 2D Space

public readonly struct Point(double x, double y)
{
    public double X { get; } = x;
    public double Y { get; } = y;
    public double Magnitude => Math.Sqrt(X * X + Y * Y);
}
public readonly struct Point(double x, double y)
{
    public double X { get; } = x;
    public double Y { get; } = y;
    public double Magnitude => Math.Sqrt(X * X + Y * Y);
}
'INSTANT VB WARNING: VB has no equivalent to the C# readonly struct:
'ORIGINAL LINE: public readonly struct Point(double x, double y)
Public Structure Point(Double x, Double y)
	Public ReadOnly Property X() As Double = x
	Public ReadOnly Property Y() As Double = y
	Public ReadOnly Property Magnitude() As Double
		Get
			Return Math.Sqrt(X * X + Y * Y)
		End Get
	End Property
End Structure
VB   C#

In this example, the primary constructor for the Point struct initializes X and Y properties, showcasing how concise and expressive the syntax can be.

Example 2: Configurable Logger with Default Settings

public class Logger(string filePath = "log.txt", LogLevel level = LogLevel.Info)
{
    private readonly string _filePath = filePath;
    private readonly LogLevel _level = level;
    public void Log(string message)
    {
        // Actual logging implementation using _filePath and _level
    }
}
public class Logger(string filePath = "log.txt", LogLevel level = LogLevel.Info)
{
    private readonly string _filePath = filePath;
    private readonly LogLevel _level = level;
    public void Log(string message)
    {
        // Actual logging implementation using _filePath and _level
    }
}
'INSTANT VB TODO TASK: The following line contains an assignment within expression that was not extracted by Instant VB:
'ORIGINAL LINE: public class Logger(string filePath = "log.txt", LogLevel level = LogLevel.Info)
Public Class Logger(String filePath = "log.txt", LogLevel level = LogLevel.Info)
	Private ReadOnly _filePath As String = filePath
	Private ReadOnly _level As LogLevel = level
	Public Sub Log(ByVal message As String)
		' Actual logging implementation using _filePath and _level
	End Sub
End Class
VB   C#

Here, the primary constructor for the Logger class provides default values for filePath and level, making it flexible and easy to use while maintaining configurability.

Example 3: Dependency Injection

public interface IService
{
    Distance GetDistance();
}
public class ExampleController(IService service) : ControllerBase
{
    public ActionResult<Distance> Get() => service.GetDistance();
}
public interface IService
{
    Distance GetDistance();
}
public class ExampleController(IService service) : ControllerBase
{
    public ActionResult<Distance> Get() => service.GetDistance();
}
Public Interface IService
	Function GetDistance() As Distance
End Interface
Public Class ExampleController(IService service)
	Inherits ControllerBase

	Public Function [Get]() As ActionResult(Of Distance)
		Return service.GetDistance()
	End Function
End Class
VB   C#

Primary constructors suit dependency injection scenarios. In this example, a controller class indicates its dependencies, enhancing maintainability and facilitating unit testing.

Example 4: Building a Geometric Shape Hierarchy

public abstract class Shape(double width, double height)
{
    public double Width { get; } = width;
    public double Height { get; } = height;
    public abstract double CalculateArea();
}
public class Rectangle(double width, double height) : Shape(width, height)
{
    public override double CalculateArea() => Width * Height;
}
public class Circle : Shape
{
    public Circle(double radius) : base(radius * 2, radius * 2) { }
    public override double CalculateArea() => Math.PI * Math.Pow(Width / 2, 2);
}
public abstract class Shape(double width, double height)
{
    public double Width { get; } = width;
    public double Height { get; } = height;
    public abstract double CalculateArea();
}
public class Rectangle(double width, double height) : Shape(width, height)
{
    public override double CalculateArea() => Width * Height;
}
public class Circle : Shape
{
    public Circle(double radius) : base(radius * 2, radius * 2) { }
    public override double CalculateArea() => Math.PI * Math.Pow(Width / 2, 2);
}
Public MustInherit Class Shape(Double width, Double height)
	Public ReadOnly Property Width() As Double = width
	Public ReadOnly Property Height() As Double = height
	Public MustOverride Function CalculateArea() As Double
End Class
Public Class Rectangle(Double width, Double height)
	Inherits Shape(width, height)

	Public Overrides Function CalculateArea() As Double
		Return Width * Height
	End Function
End Class
Public Class Circle
	Inherits Shape

	Public Sub New(ByVal radius As Double)
		MyBase.New(radius * 2, radius * 2)
	End Sub
	Public Overrides Function CalculateArea() As Double
		Return Math.PI * Math.Pow(Width / 2, 2)
	End Function
End Class
VB   C#

In this example, the primary constructor in the Shape class forms the foundation for a geometric shape hierarchy. Subclasses like Rectangle and Circle leverage the primary constructor for consistent initialization. The Rectangle class itself declares the primary constructor and passes the captured primary constructor parameters to the Shape class primary parameters. The Circle class showcases flexibility by defining its constructor within the whole class and then passing its parameters as default values for the Shape constructor using the base keyword.

Introducing IronPDF

IronPDFis a versatile C# library that empowers developers to create, manipulate, and convert PDF files effortlessly. Whether you're generating invoices, reports, or any other document, IronPDF allows you to seamlessly convert HTML content into polished and professional PDFs directly within your C# application.

C# Primary Constructor (How It Works For Developer): Figure 1 - IronPDF webpage

Installing IronPDF: A Quick Start

To incorporate IronPDF into your C# project, begin by installing the IronPDF NuGet package. Execute the following command in your Package Manager Console:

Install-Package IronPdf

Alternatively, locate "IronPDF" in the NuGet Package Manager and proceed with the installation from there.

C# Primary Constructor (How It Works For Developer): Figure 2 - Searching for the IronPDF package in the NuGet package manager browser

Generating PDFs with IronPDF

Creating a PDF using IronPDF is a streamlined process. Consider the following example:

var htmlContent = "<html><body><h1>Hello, IronPDF!</h1></body></html>";
// Create a new PDF document
var pdfDocument = new IronPdf.ChromePdfRenderer();
pdfDocument.RenderHtmlAsPdf(htmlContent).SaveAs("C:/GeneratedDocument.pdf");
var htmlContent = "<html><body><h1>Hello, IronPDF!</h1></body></html>";
// Create a new PDF document
var pdfDocument = new IronPdf.ChromePdfRenderer();
pdfDocument.RenderHtmlAsPdf(htmlContent).SaveAs("C:/GeneratedDocument.pdf");
Dim htmlContent = "<html><body><h1>Hello, IronPDF!</h1></body></html>"
' Create a new PDF document
Dim pdfDocument = New IronPdf.ChromePdfRenderer()
pdfDocument.RenderHtmlAsPdf(htmlContent).SaveAs("C:/GeneratedDocument.pdf")
VB   C#

In this example, IronPDF is utilized to render HTML content into a PDF document, subsequently saved to the specified location. For more details on creating and manipulating PDFs in C#, please visit this complete tutorial link, and to explore more please visit this documentation page.

C# Primary Constructors: A Class Initialization Revolution

C# Primary Constructors offer a declarative and streamlined approach to initializing class properties directly within the class declaration. Let's explore whether this elegant feature can be seamlessly integrated with IronPDF.

Integration of C# Primary Constructors with IronPDF

While C# Primary Constructors are primarily a language feature focused on class initialization, their direct integration with IronPDF may not be a common use case. IronPDF's core functionality lies in the generation and manipulation of PDF documents, and the specifics of class initialization might not directly align with this workflow.

However, developers can leverage C# Primary Constructors when defining custom classes or structures related to IronPDF configurations or data models. For instance, if your application requires a specific class structure to manage PDF-related settings or configurations, C# Primary Constructors can be a valuable tool for initializing these classes concisely.

public class PdfGenerationSettings(string title, bool includeHeader, bool includeFooter)
{
    public string Title { get; } = title;
    public bool IncludeHeader { get; } = includeHeader;
    public bool IncludeFooter { get; } = includeFooter;
    // Additional properties...
}
// Usage with IronPDF
var pdfSettings = new PdfGenerationSettings("My PDF Title", true, false);
var renderOptions = new ChromePdfRenderOptions();
// Apply settings from PdfGenerationSettings
renderOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
renderOptions.MarginTop = 20;
renderOptions.MarginBottom = 20;
renderOptions.MarginLeft = 10;
renderOptions.MarginRight = 10;
renderOptions.Title = pdfSettings.Title ?? string.Empty;
if (pdfSettings.IncludeHeader)
{
    renderOptions.TextHeader = new TextHeaderFooter
    {
        CenterText = "Page {page} of {total-pages}",
        DrawDividerLine = true
    };
}
var pdfDocument = new IronPdf.ChromePdfRenderer();
pdfDocument.RenderingOptions = renderOptions;
pdfDocument.RenderHtmlAsPdf("<html><body><h1>Hello, IronPDF!</h1></body></html>").SaveAs("CustomizedDocument.pdf");
public class PdfGenerationSettings(string title, bool includeHeader, bool includeFooter)
{
    public string Title { get; } = title;
    public bool IncludeHeader { get; } = includeHeader;
    public bool IncludeFooter { get; } = includeFooter;
    // Additional properties...
}
// Usage with IronPDF
var pdfSettings = new PdfGenerationSettings("My PDF Title", true, false);
var renderOptions = new ChromePdfRenderOptions();
// Apply settings from PdfGenerationSettings
renderOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
renderOptions.MarginTop = 20;
renderOptions.MarginBottom = 20;
renderOptions.MarginLeft = 10;
renderOptions.MarginRight = 10;
renderOptions.Title = pdfSettings.Title ?? string.Empty;
if (pdfSettings.IncludeHeader)
{
    renderOptions.TextHeader = new TextHeaderFooter
    {
        CenterText = "Page {page} of {total-pages}",
        DrawDividerLine = true
    };
}
var pdfDocument = new IronPdf.ChromePdfRenderer();
pdfDocument.RenderingOptions = renderOptions;
pdfDocument.RenderHtmlAsPdf("<html><body><h1>Hello, IronPDF!</h1></body></html>").SaveAs("CustomizedDocument.pdf");
Public Class PdfGenerationSettings(String title, Boolean includeHeader, Boolean includeFooter)
	Public ReadOnly Property Title() As String = title
	Public ReadOnly Property IncludeHeader() As Boolean = includeHeader
	Public ReadOnly Property IncludeFooter() As Boolean = includeFooter
	' Additional properties...
End Class
' Usage with IronPDF
Private pdfSettings = New PdfGenerationSettings("My PDF Title", True, False)
Private renderOptions = New ChromePdfRenderOptions()
' Apply settings from PdfGenerationSettings
renderOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4
renderOptions.MarginTop = 20
renderOptions.MarginBottom = 20
renderOptions.MarginLeft = 10
renderOptions.MarginRight = 10
renderOptions.Title = If(pdfSettings.Title, String.Empty)
If pdfSettings.IncludeHeader Then
	renderOptions.TextHeader = New TextHeaderFooter With {
		.CenterText = "Page {page} of {total-pages}",
		.DrawDividerLine = True
	}
End If
Dim pdfDocument = New IronPdf.ChromePdfRenderer()
pdfDocument.RenderingOptions = renderOptions
pdfDocument.RenderHtmlAsPdf("<html><body><h1>Hello, IronPDF!</h1></body></html>").SaveAs("CustomizedDocument.pdf")
VB   C#

In this example, the PdfGenerationSettings class utilizes a C# Primary Constructor to initialize properties related to PDF generation settings. Later that can be used to figure out which rendering options to add and which to skip. The output contains a header text and title as it was set using the primary constructor parameter.

C# Primary Constructor (How It Works For Developer): Figure 3 - Output PDF from the code example above

Conclusion

In conclusion, Primary Constructors in C# present a refined and expressive approach to class initialization. Their declarative syntax enhances code readability, promotes immutability, and simplifies the process of creating objects with default values. Whether you're defining properties, enforcing immutability, or embracing default values, Primary Constructors empower developers to master the art of class initialization in the dynamic world of C# programming.

While the direct integration of C# Primary Constructors with IronPDF might not be the main focus, these two elements can work together harmoniously. C# Primary Constructors enhance the clarity and simplicity of class initialization, making them valuable for defining structures or configurations related to IronPDF workflows.

Leverage the power of IronPDF for robust PDF generation, and employ C# Primary Constructors where class initialization elegance is paramount. This dynamic duo empowers you to navigate the complexities of document generation with creativity and efficiency in the vibrant world of C# programming.

IronPDF offers a free trial and its lite license starts from $749.