.NET HELP

C# Indexers (How It Works For Developers)

Kannaopat Udonpant
Kannapat Udonpant
October 23, 2024
Share:

An indexer in C# is a special type of property that makes instances of a class or struct to be accessed using the array access operator []. Indexers can be very useful in creating "smart arrays" or encapsulating data in a simplified syntax. They provide a way to use instances of a class just like you would use arrays, where you can access data via an index. This article will explore how to declare and use C# indexers with practical examples. And we'll also explore the IronPDF library at the end of the article.

Basic Indexer Syntax

An indexer is an instance member using the 'this' keyword in a class or struct, followed by the indexer declaration. You also specify the parameter types and the return type. The general syntax for an indexer instance member looks like this:

public return_type this[parameter_type index]
{
    get
    {
        // code to return data
    }
    set
    {
        // set accessor, code to set data
    }
}
public return_type this[parameter_type index]
{
    get
    {
        // code to return data
    }
    set
    {
        // set accessor, code to set data
    }
}

Here, return_type is the value type that the indexer will return such as an integer value, and parameter_type is the type of the index, often an int. The get accessor returns the value at the specified index, and the set block code accessor assigns a value to that index.

Indexer Declaration and Use

We'll examine a basic illustration of implementing an indexer within a C# class. Consider a class Program that encapsulates an array of strings.

class Program
{
    private string[] values = new string[5]; // Array with 5 elements
    public string this[int index]
    {
        get
        {
            return values[index];
        }
        set
        {
            values[index] = value;
        }
    }
}
class Program
{
    private string[] values = new string[5]; // Array with 5 elements
    public string this[int index]
    {
        get
        {
            return values[index];
        }
        set
        {
            values[index] = value;
        }
    }
}

In the above code:

  • The Program class contains a string array named values.
  • The string this[int index] is the indexer declaration, where the int index is the index parameterized property used to access elements of the array.
  • The get accessor returns the indexed value at the specified index, and the set accessor assigns an indexed value to that index.
  • The set accessor sets the value at the given index.

This means you can create an instance of the Program class and access its values array using the indexer, like this:

class Program
{
    static void Main()
    {
        Program program = new Program();
        // Set values using indexer
        program[0] = "First";
        program[1] = "Second";
        // Access values using indexer
        Console.WriteLine(program[0]); // Output: First
        Console.WriteLine(program[1]); // Output: Second
    }
}
class Program
{
    static void Main()
    {
        Program program = new Program();
        // Set values using indexer
        program[0] = "First";
        program[1] = "Second";
        // Access values using indexer
        Console.WriteLine(program[0]); // Output: First
        Console.WriteLine(program[1]); // Output: Second
    }
}

In this code, you see that the indexer provides a simple syntax for accessing the values array, similar to how you would access elements in an array.

Understanding the get and set Accessors

The get and set accessors inside the indexer are like block code that allows you to retrieve and assign data similarly to how you would with properties. The main difference is that indexers use an index parameter to work with collections of data rather than individual data members.

The get block is responsible for returning the data at the specified index, while the set block assigns data to the specified index. Here’s another example to solidify your understanding:

class StudentRecords
{
    private string[] studentNames = new string[3];
    public string this[int index]
    {
        get
        {
            if (index >= 0 && index < studentNames.Length)
            {
                return studentNames[index];
            }
            return "Invalid Index";
        }
        set
        {
            if (index >= 0 && index < studentNames.Length)
            {
                studentNames[index] = value;
            }
        }
    }
    public int Length
    {
        get { return studentNames.Length; }
    }
}
class StudentRecords
{
    private string[] studentNames = new string[3];
    public string this[int index]
    {
        get
        {
            if (index >= 0 && index < studentNames.Length)
            {
                return studentNames[index];
            }
            return "Invalid Index";
        }
        set
        {
            if (index >= 0 && index < studentNames.Length)
            {
                studentNames[index] = value;
            }
        }
    }
    public int Length
    {
        get { return studentNames.Length; }
    }
}

In this example:

  • The StudentRecords class has a private string[] studentNames array that holds the names of students.
  • The indexer checks if the index is within the bounds of the array before setting or retrieving the value.
  • An int type Length property provides access to the length of the array.

You can use this class in the Main method as follows:

class Program
{
    public static void Main()
    {
        StudentRecords records = new StudentRecords();
        // Set values using indexer
        records[0] = "John";
        records[1] = "Jane";
        records[2] = "Bob";
        // Access values using indexer
        for (int i = 0; i < records.Length; i++)
        {
            Console.WriteLine(records[i]);
        }
    }
}
class Program
{
    public static void Main()
    {
        StudentRecords records = new StudentRecords();
        // Set values using indexer
        records[0] = "John";
        records[1] = "Jane";
        records[2] = "Bob";
        // Access values using indexer
        for (int i = 0; i < records.Length; i++)
        {
            Console.WriteLine(records[i]);
        }
    }
}

Creating a Generic Indexer

You can also create generic classes with indexers, allowing your code to handle multiple data types. Here’s a simple example of a generic class with a generic indexer:

class GenericClass<T>
{
    private T[] elements = new T[5];
    public T this[int index]
    {
        get
        {
            return elements[index];
        }
        set
        {
            elements[index] = value;
        }
    }
    public int Length
    {
        get { return elements.Length; }
    }
}
class GenericClass<T>
{
    private T[] elements = new T[5];
    public T this[int index]
    {
        get
        {
            return elements[index];
        }
        set
        {
            elements[index] = value;
        }
    }
    public int Length
    {
        get { return elements.Length; }
    }
}

In this code:

  • The GenericClassclass defines an indexer that can work with any data type.
  • The "this[int index]" indexer allows you to access the elements in the array, regardless of the type.

You can now use the GenericClass with different data types in the Main method:

class Program
{
    public static void Main()
    {
        GenericClass<int> intArray = new GenericClass<int>();
        intArray[0] = 10;
        intArray[1] = 20;
        GenericClass<string> stringArray = new GenericClass<string>();
        stringArray[0] = "Hello";
        stringArray[1] = "World";
        // Output the integer array values
        for (int i = 0; i < intArray.Length; i++)
        {
            Console.WriteLine(intArray[i]);
        }
        // Output the string array values
        for (int i = 0; i < stringArray.Length; i++)
        {
            Console.WriteLine(stringArray[i]);
        }
    }
}
class Program
{
    public static void Main()
    {
        GenericClass<int> intArray = new GenericClass<int>();
        intArray[0] = 10;
        intArray[1] = 20;
        GenericClass<string> stringArray = new GenericClass<string>();
        stringArray[0] = "Hello";
        stringArray[1] = "World";
        // Output the integer array values
        for (int i = 0; i < intArray.Length; i++)
        {
            Console.WriteLine(intArray[i]);
        }
        // Output the string array values
        for (int i = 0; i < stringArray.Length; i++)
        {
            Console.WriteLine(stringArray[i]);
        }
    }
}

Using IronPDF with C# Indexer

C# Indexers (How It Works For Developers): Figure 1 - IronPDF

IronPDF is a C# library designed for generating, editing, and converting PDFs in .NET applications. It simplifies working with PDFs for developers to create PDFs from HTML, manipulate PDF files, and handle advanced functionalities such as merging, printing, and adding signatures programmatically.

You can leverage IronPDF within your C# programs that utilize indexers to dynamically generate and manage PDF content. For example, suppose you have a class that holds HTML strings, and you want to generate PDFs for each HTML entry using an indexer. This approach streamlines PDF generation while keeping your code organized and intuitive.

using IronPdf;
using System;
class PdfGenerator
{
    private string[] htmlTemplates = new string[3];
    public string this[int index]
    {
        get { return htmlTemplates[index]; }
        set { htmlTemplates[index] = value; }
    }
    public void GeneratePdf(int index, string outputPath)
    {
        var renderer = new ChromePdfRenderer();
        var pdfDocument = renderer.RenderHtmlAsPdf(this[index]); // Access HTML string using indexer
        pdfDocument.SaveAs(outputPath);
    }
}
class Program
{
    public static void Main()
    {
        PdfGenerator pdfGen = new PdfGenerator();
        // Populate HTML templates
        pdfGen[0] = "<h1>First Document</h1><p>This is the first PDF.</p>";
        pdfGen[1] = "<h1>Second Document</h1><p>This is the second PDF.</p>";
        pdfGen[2] = "<h1>Third Document</h1><p>This is the third PDF.</p>";
        // Generate PDFs using the indexer
        pdfGen.GeneratePdf(0, "first.pdf");
        pdfGen.GeneratePdf(1, "second.pdf");
        pdfGen.GeneratePdf(2, "third.pdf");
        Console.WriteLine("PDFs generated successfully.");
    }
}
using IronPdf;
using System;
class PdfGenerator
{
    private string[] htmlTemplates = new string[3];
    public string this[int index]
    {
        get { return htmlTemplates[index]; }
        set { htmlTemplates[index] = value; }
    }
    public void GeneratePdf(int index, string outputPath)
    {
        var renderer = new ChromePdfRenderer();
        var pdfDocument = renderer.RenderHtmlAsPdf(this[index]); // Access HTML string using indexer
        pdfDocument.SaveAs(outputPath);
    }
}
class Program
{
    public static void Main()
    {
        PdfGenerator pdfGen = new PdfGenerator();
        // Populate HTML templates
        pdfGen[0] = "<h1>First Document</h1><p>This is the first PDF.</p>";
        pdfGen[1] = "<h1>Second Document</h1><p>This is the second PDF.</p>";
        pdfGen[2] = "<h1>Third Document</h1><p>This is the third PDF.</p>";
        // Generate PDFs using the indexer
        pdfGen.GeneratePdf(0, "first.pdf");
        pdfGen.GeneratePdf(1, "second.pdf");
        pdfGen.GeneratePdf(2, "third.pdf");
        Console.WriteLine("PDFs generated successfully.");
    }
}

C# Indexers (How It Works For Developers): Figure 2 - Console Output

Conclusion

C# indexers are a useful feature that helps you to make your classes and structs behave like arrays. By providing simplified syntax and flexible data access, you can create more intuitive and readable code. Whether you’re working with strings, integers, or any other data type, indexers give you the ability to encapsulate your data structure and access it using indices cleanly and efficiently.

IronPDF makes it easy to get started with a free trial that gives you access to all the features you need to create, manipulate, and render PDFs. You can take your time exploring the software, and once you're satisfied, licenses are available starting at $749.

Kannaopat Udonpant
Software Engineer
Before becoming a Software Engineer, Kannapat completed a Environmental Resources PhD from Hokkaido University in Japan. While pursuing his degree, Kannapat also became a member of the Vehicle Robotics Laboratory, which is part of the Department of Bioproduction Engineering. In 2022, he leveraged his C# skills to join Iron Software's engineering team, where he focuses on IronPDF. Kannapat values his job because he learns directly from the developer who writes most of the code used in IronPDF. In addition to peer learning, Kannapat enjoys the social aspect of working at Iron Software. When he's not writing code or documentation, Kannapat can usually be found gaming on his PS5 or rewatching The Last of Us.
< PREVIOUS
C# New GUID (How It Works For Developers)
NEXT >
C# foreach with index (How It Works For Developers)