Partial Properties and Indexers in C#

Partial Properties and Indexers in C# with Examples

In this article, I will discuss Partial Properties and Indexers in C# with Examples. Please read our previous articles discussing Thread Synchronization with Advanced Lock in C# with Examples. In C# 13, Partial Properties and Indexers were introduced to allow for better code organization and the separation of logic in scenarios such as code generation or when working with complex systems that require modularity in class definitions. This feature allows partial classes to contain partial properties and partial indexers, enabling easier extension and modification of code without needing to modify the entire class or structure.

What Are Properties?

In C#, properties provide a mechanism to read, write, or compute a class’s values of private fields. They are typically used to encapsulate data, allowing controlled access to class members. A property consists of a getter and/or setter. The getter is used to return the value of a field, and the setter is used to assign a value to the field. For a better understanding, please check the code below:

public class Person
{
    private string name;

    // Property with a getter and setter
    public string Name
    {
        get { return name; }
        set { name = value; }
    }
}
What Are Indexers?

An indexer allows an object to be accessed like an array, using square brackets ([]). It enables you to define how to retrieve or set values within an object using an index, often simplifying the code. For a better understanding, please check the code below:

public class MyCollection
{
    private int[] data = new int[10];

    // Indexer to access elements like an array
    public int this[int index]
    {
        get { return data[index]; }
        set { data[index] = value; }
    }
}
When to Use Properties and Indexers?
Use properties when:
  • You need to encapsulate data while allowing controlled access to private fields.
  • You want to ensure validation or apply business rules when setting or getting data.
  • You need to add logic to access or modify the values.
Use indexers when:
  • You want to allow objects of a class to be indexed like arrays.
  • The object represents a collection or sequence, and you want to access individual elements in a way that is intuitive and resembles accessing an array.
  • You need to create an object that mimics the behavior of a list, dictionary, or other indexed structure.
Example to Understand Properties and Indexers Before C# 13:

The following example demonstrates how properties and indexers were used before C# 13 using a .NET Core Console Application. The following code is self-explained, so please read the comment lines for a better understanding.

namespace CSharp13NewFeatures
{
    public class Employee
    {
        // Private field
        private string name;

        // Property to get and set name
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
    }

    public class EmployeeCollection
    {
        // Private array to store employee names
        private string[] employees = new string[10];

        // Indexer to access employee names by index
        public string this[int index]
        {
            get { return employees[index]; }
            set { employees[index] = value; }
        }
    }

    public class Program
    {
        static void Main(string[] args)
        {
            // Example for property
            Employee emp = new Employee();
            emp.Name = "Pranaya Rout";
            Console.WriteLine($"Employee Name: {emp.Name}");

            // Example for indexer
            EmployeeCollection collection = new EmployeeCollection();
            collection[0] = "Rakesh";
            collection[1] = "Kumar";
            Console.WriteLine($"Employee 1: {collection[0]}");
            Console.WriteLine($"Employee 2: {collection[1]}");
        }
    }
}
Code Explanations:
  • Properties: The Employee class has a Name property used to get and set the private field name. This is a standard use of properties for encapsulating data.
  • Indexers: The EmployeeCollection class uses an indexer to access the employees array as if it were an array. It uses the this[int index] syntax to define how to get and set employee names by index.
Output:

Example to Understand Properties and Indexers Before C# 13

What are the New Partial Properties and Indexers in C# 13?

In C# 13, partial properties and partial indexers were introduced as part of partial class definitions. This new feature allows properties and indexers to be defined across multiple files or sections within the same class, making it easier to manage large codebases and extend automatically generated classes without directly modifying them.

Partial Properties:
  • A partial property allows the property definition (getter/setter) to be split across multiple parts of a partial class.
  • You can declare the property in one part and define its getter and setter in another part. However, the property must be fully declared in one part (with get; set; or as a partial property) and implemented in another part (with explicit getter and setter).
Partial Indexers:
  • Similarly, a partial indexer allows its definition to be split across multiple parts of a class.
  • The indexer declaration is placed in one part, and the implementation (getter/setter) is provided in another.
  • You cannot define multiple indexers with the same signature in different parts of the class.
Example to Understand Partial Properties and Indexers in C#:
Part-1 of the class in Product1.cs

Create a class file named Product1.cs and then copy and paste the following code:

public partial class Product
{
    private int price;

    // Partial property: the getter and setter can be defined in separate parts
    public partial int Price { get; set; }
}
Part-2 of the class in Product2.cs

Create a class file named Product2.cs and then copy and paste the following code:

public partial class Product
{
    // Complete the partial property: implementing the getter and setter
    public partial int Price
    {
        get { return price; }
        set { price = value; }
    }
}
Part-3 of the class in Product3.cs

Create a class file named Product3.cs and then copy and paste the following code:

public partial class Product
{
    private string[] productNames = new string[10];

    // Partial indexer: declaring the indexer for accessing product names by index
    public partial string this[int index]
    {
        get { return productNames[index]; }
        set { productNames[index] = value; }
    }
}
Part-4 of the class in Product4.cs

Create a class file named Product4.cs and then copy and paste the following code:

public partial class Product
{
    // Complete the partial indexer: implementing the getter and setter for the indexer
    public partial string this[int index]
    {
        get { return productNames[index]; }
        set { productNames[index] = value; }
    }
}
Explanation of the Example:
Partial Property:
  • Declaration: In Part 1 of the class, the Price property is declared as public partial int Price { get; set; }.
  • Implementation: In Part 2, the Price property is implemented, providing the actual logic for the getter and setter.

The partial property feature allows the getter and setter to be written in different parts of the code, which is useful for extending generated classes or dividing the code into smaller, more manageable files.

Partial Indexer:
  • Declaration: In Part 3, we declare the indexer public partial string this[int index] but don’t provide any logic.
  • Implementation: In Part 4, we implement the getter and setter for the indexer, allowing us to access the productNames array by index.

This demonstrates how partial indexers allow you to define complex access patterns for collections across different parts of the class.

Real-Time Scenario Using Partial Properties and Indexers in C#

Imagine you are building an e-commerce application using Entity Framework. The Product class generated by EF contains properties like ProductName, Price, CategoryId, etc. However, you need to add some logic or properties for business-specific calculations like DiscountedPrice.

You can define the DiscountedPrice as a partial property across different parts of the class, making your logic clean and easy to manage, without touching the generated code. The following example code is self-explained, so please read the comment lines for a better understanding.

namespace CSharp13NewFeatures
{
    // Part 1 of Product class (generated file)
    public partial class Product
    {
        // Declare a partial property for Price
        public partial decimal Price { get; set; }
    }

    // Part 2 of Product class (user-defined logic)
    public partial class Product
    {
        private decimal price;

        // Implement the partial property for Price
        public partial decimal Price
        {
            get { return price; }
            set { price = value; }
        }
    }

    // Part 3 of Product class (indexer declaration)
    public partial class Product
    {
        private string[] productNames = new string[10];

        // Declare a partial indexer for product names
        public partial string this[int index] { get; set; }
    }

    // Part 4 of Product class (indexer implementation)
    public partial class Product
    {
        // Implement the partial indexer for product names
        public partial string this[int index]
        {
            get { return productNames[index]; }
            set { productNames[index] = value; }
        }
    }

    public class Program
    {
        static void Main()
        {
            // Create a product instance
            Product product = new Product();
            product.Price = 299.99m;
            Console.WriteLine($"Product Price: {product.Price}");

            // Using the indexer to set and get product names
            product[0] = "Laptop";
            Console.WriteLine($"Product 0 Name: {product[0]}");
        }
    }
}
Code Explanation:
Partial Property for Price:

The Price property is declared and defined across two parts of the Product class:

  • In Part 1, the property is declared as public partial decimal Price { get; set; }.
  • In Part 2, the getter and setter are implemented.
Partial Indexer for Product Names:

The indexer is declared and implemented across two parts of the Product class:

  • In Part 3, the indexer is declared as public partial string this[int index] { get; set; }.
  • In Part 4, the getter and setter for the indexer are implemented.
Code Flow:

In the Main method, the Price property is set and retrieved, and the indexer is used to set and retrieve product names at specific indices. This shows how partial properties and indexers allow for modular class design and can be split across multiple files in large projects.

Output:

Real-Time Scenarios and Use Cases for Partial Properties and Indexers in C#

Key Points to Remember:
  • Partial properties and indexers: You can split the definition of properties and indexers across multiple parts of a partial class, but only one part can define the getter and setter (or getter/setter logic) for them.
  • No duplicate declarations: You cannot define a partial property or indexer multiple times in different parts with the same signature.
  • This allows you to extend or modify auto-generated classes (e.g., models from ORM frameworks like Entity Framework) without directly modifying the generated files.

Partial properties and indexers in C# 13 allow you to extend and modularize your classes across multiple files, making it easier to manage large codebases, add business logic to auto-generated classes, and collaborate in a team. This feature is especially useful in environments where code generation tools or large classes with many properties are in play, allowing for cleaner, more maintainable code.

In the next article, I will discuss Improved Support for ref and unsafe in Async Methods and Iterators in C# with Examples. In this article, I explain Partial Properties and Indexers in C# with Examples. I would like your feedback. Please post your feedback, questions, or comments about this article.

Leave a Reply

Your email address will not be published. Required fields are marked *