C# Tutorial

C# Tutorial C# First Application C# Variables C# Data Types C# Operators C# Keywords

C# Control Statement

C# If Statements C# Switch Statements C# for Loop C# While Loop C# do While loop C# Jump Statements C# Function C# functions with out variable

C# Arrays

C# Arrays

C# Function

C# Function call by value C# Call by reference C# Passing array to function C# Multidimensional Arrays C# Jagged Arrays C# Params C# Array Class C# Command Line Arguments

C# Object Class

C# Object and Classes C# Constructors C# Destructor C# this Keyword C# static field C# static class C# Static Constructor C# Structs C# enum C# Properties

C# Inheritance

C# Inheritance C# Multilevel Inheritance C# Aggregation C# Member overloading C# Method Overriding C# Base

C# Polymorphism

C# Polymorphism C# Sealed

C# Abstraction

C# Abstraction C# Interface

C# Namespace

C# Namespace C# Access Modifiers C# Encapsulation

C# Strings

C# String

C# Misc

C# Design Patterns Dictionary in C# Boxing and Unboxing in C# Ref and Out in C# Serialization in C# Dispose and Finalize in C# CONSOLE LOG IN C# Get File extension in C# Insert query in c# Difference Between List and Dictionary in C# Getters and Setters in C# Extension Methods in C# Insert query in c# CONSOLE LOG IN C# Get File extension in C# Random.NextDouble() Method in C# Binary Search in C# Difference between Delegates and Interfaces in C# Double.IsFinite() Method in C# Index Constructor in C# Abstraction in C# Basic OOPS Concepts In C# Queue.CopyTo() Method in C# single.compareto() method in C# C# Throw Exception in Constructor DECODE IN C# file.setlastwritetimeutc() method in C# Convert Object to List in C# convert.ToSByte(string, IFormatProvider) Method in C# C# Declare Delegate in Interface console.TreatControl C As Input property in C# Copying the queue elements to 1-D Array in C# Array.Constrainedcopy() Method in C# C# in and out Char.IsLetterOrDigit() method in C# Debugging in C# decimal.compare() method in C# Difference between Console.Read and Console.Readline in C# Metadata in C# C# Event Handler Example Default Interface Methods in C# Difference between hashtable and dictionary in C# C# program to implement IDisposable Interface Encapsulation in C# SortedList.IndexOfVaalue(Object) Method in C# Hash Maps in C# How to clear text files in C# How to Convert xls to xlsx in C# Foreach loop in C# FIFO in C# How to handle null exception in C# Type.Is Instance Of Type() Method in C# How to add data into MySQL database using C# How to use angular js in ASP net Csharp decimal.compare() method in Csharp Difference between Console.Read and Console.Readline in Csharp How to Implement Interface in Csharp char.IsUpper() Method in C# Index Of Any() Method in C# Quantifiers in C# C# program to Get Extension of a Given File C# Error Logging C# ENCRIPTION Can we create an object for Abstract Class in C# Console.CursorVisible in C# SortedDictionary Implementation in C# C# Hash Table with Examples Setting the Location of the Label in c# Collections in c# Virtual Keyword in C# Reverse of string in C# String and StringBuilder in C# Encapsulation in C# SortedList.IndexOfVaalue(Object) Method in C# Hash Maps in C# How to clear text files in C# How to Convert xls to xlsx in C# Foreach loop in C# FIFO in C# How to handle null exception in C# Type.Is Instance Of Type() Method in C# How to add data into MySQL database using C# How to use angular js in ASP net Csharp decimal.compare() method in Csharp Difference between Console.Read and Console.Readline in Csharp How to Implement Interface in Csharp char.IsUpper() Method in C# Index Of Any() Method in C# Quantifiers in C# C# program to Get Extension of a Given File Difference between ref and out in C# Singleton Class in C# Const And Readonly In Csharp BinaryReader and BinaryWriter in C# C# Attributes C# Delegates DirectoryInfo Class in C# Export and Import Excel Data in C# File Class in C# FileInfo Class in C# How to Cancel Parallel Operations in C#? Maximum Degree of Parallelism in C# Parallel Foreach Loop in C# Parallel Invoke in C# StreamReader and StreamWriter in C# TextReader and TextWriter in C# AsQueryable() in C# Basic Database Operations Using C# C# Anonymous Methods C# Events C# Generics C# Indexers C# Multidimensional Indexers C# Multithreading C# New Features C# Overloading of Indexers Difference between delegates and events in C# Operator overloading in C# Filter table in C# C# Queue with Examples C# Sortedlist With Examples C# Stack with Examples C# Unsafe Code File Handling in C# HashSet in C# with Examples List Implementation in C# SortedSet in C# with Examples C# in Depth Delegates and Events in C# Finally Block in C# How to Split String in C# Loggers in C# Nullable Types in C# REVERSE A STRING IN C# TYPE CASTING IN C# What is Generics in C# ABSTRACT CLASS IN C# Application of pointer in C# Await in c# READONLY AND CONSTANT IN C# Type safe in C# Types of Variables in c# Use of delegates in c# ABSTRACT CLASS IN C# Application of pointer in C# Await in c# READONLY AND CONSTANT IN C# Type safe in C# Types of Variables in c# Use of delegates in c# ABSTRACT CLASS IN C# Application of pointer in C# Await in c# READONLY AND CONSTANT IN C# Type safe in C# Types of Variables in c# Use of delegates in c# Atomic Methods Thread Safety and Race Conditions in C# Parallel LINQ in C# Design Principles in C# Difference Between Struct And Class In C# Difference between Abstraction and Encapsulation in C# Escape Sequence Characters in C# What is IOC in C# Multiple Catch blocks in C# Appdomain in C# Call back methods in C# Change Datetime format in C# Declare String array in C# Default Access Specifier in c# Foreach in LINQ C# How to compare two lists in C# How to Convert String to Datetime in c# How to get only Date from DateTime in C# Ispostback in asp net C# JSON OBJECT IN C# JSON STRINGIFY IN C# LAMBDA FUNCTION IN C# LINQ Lambda Expression in C# Microservices in C# MSIL IN C# Reference parameter in C# Shadowing(Method hiding) in C# Solid principles in C# Static Members in C# Task run in C# Transaction scope in C# Type Conversion in c# Unit of Work in C# Unit Test Cases in c# User Defined Exception in c# Using Keyword in C# Var Keyword in C# What is gac in C#

C# Indexers

Indexers in C# offer an array-like syntax for accessing the elements or properties of a class or struct. They provide an easy way to access and alter data inside a class by enabling class instances to be indexed like an array.

A class or struct's Indexer is a unique component that makes it possible to index objects as arrays. It uses the well-known array access syntax to obtain or assign values represented by square brackets ([]). It enables you to use an object as a virtual array, to put it simply.

Comparison with Arrays and Properties

Indexers are similar to arrays because they allow you to access elements by an index. However, unlike arrays that store data directly, indexers enable you to define custom logic for retrieving or setting values associated with specific indices.

On the other hand, indexers are different from properties. While properties provide access to class members using get and set accessors, indexers can accept parameters and allow you to access multiple elements or properties through a single syntax.

Indexers provide a more intuitive and expressive way to interact with objects, allowing you to access their internal data or properties in a way that resembles array access. This can make code more readable and maintainable, especially when dealing with collections or complex data structures.

Syntax

Indexer Declaration:

In C#, this keyword is used to define an indexer, which is then followed by a parameter list enclosed in square brackets ([]). The return type and the type of indexing parameters are specified in the indexer declaration. Here's the syntax:

return Type this[indexParameterList]

{

    get

    {

        // Getter code

    }

    set

    {

        // Setter code

    }

}
  • returnType: The type of the value returned by the Indexer.
  • indexParameterList: The list of parameters used for indexing.

The get accessor retrieves the value associated with the specified index, while the set accessor assigns a value to the specified index.

Working with Index Parameters

You can provide the parameters needed to access the elements or properties of the class or struct using index parameters in an indexer. The element or property to get or alter can be chosen using these arguments, which can be of any valid C# type.

The following should be considered when working with index parameters:

  • The index arguments should be constant between the getter and setter regarding their number, types, and order.
  • You can use several index parameters to accommodate multidimensional or bespoke indexing scenarios.
  • Any legal C# type, including value, reference, and even user-defined types, may be used for the index parameters.
  • Square brackets ([]) encapsulate the index parameters.
  • You can specify the criteria for accessing elements or properties using the Indexer using the index parameters. You can retrieve or alter the desired data from a class or struct by giving the index parameters the proper values.

Note: Remember to handle any necessary validations or error handling within the getter and setter code to ensure the integrity of the indexed data.

Implementing Indexers

Creating Indexers in Classes and Structs:

To implement an indexer in a class or struct, you declare the Indexer following the syntax mentioned earlier. Here's an example:

class MyClass

{

    private int[] values = new int[10];

    public int this[int index]

    {

        get

        {

            // Getter code

            return values[index];

        }

        set

        {

            // Setter code

            values[index] = value;

        }

    }

}

The class MyClass in this example has an indexer that enables accessing elements using an integer index. The data is stored in an internal array of values, and the getter and setter accessors provide the necessary logic for values to be retrieved and assigned based on the supplied index.

Handling Multiple Index Parameters

Multiple parameter indexers in C# enable more sophisticated indexing scenarios. This is helpful when working with multidimensional data or when you require additional indexing criteria. Here's an example:

class Indexer

{

    private int[,] matrix = new int[3, 3];

    public int this[int row, int column]

    {

        get

        {

            // Getter code

            return matrix[row, column];

        }

        set

        {

            // Setter code

            matrix[row, column] = value;

        }

    }

}

In this example, a two-dimensional indexer is part of the class Indexer. The 'this' Indexer needs two parameters to access matrix elements: row and column. The getter and setter accessors fetch and assign values based on the supplied row and column indices.

Overloading Indexers:

Indexers provide a similar overloading option as methods do. This enables you to define numerous indexers inside a class or struct, each with a unique set of parameters or types. Different methods of accessing and manipulating the class's data may be made available by overloading the indexers. Here's an illustration:

class MyCollection

{

    private List<string> items = new List<string>();

    public string this[int index]

    {

        get

        {

            // Getter code

            return items[index];

        }

        set

        {

            // Setter code

            items[index] = value;

        }

    }

    public string this[string key]

    {

        get

        {

            // Getter code

            return items.FirstOrDefault(item => item.Contains(key));

        }

        set

        {

            // Setter code

            var index = items.FindIndex(item => item.Contains(key));

            if (index >= 0)

                items[index] = value;

            else

                items.Add(value);

        }

    }

}

The MyCollection class in this example has two indexers. While the second Indexer makes it possible to access elements by a specific key, the first Indexer only permits accessing elements by their numerical index. By overburdening the indexers, you can offer several ways to access and change the collection's elements depending on various criteria.

Accessing elements through indexers

The get and set accessors, which C# indexers provide, let you get and change values related to particular indices. The following describes how to access elements using indexers:

Retrieving values using the Get Accessor

The Get Accessor can be used to retrieve a value from an indexer. When you index an instance of a class or struct to get an element, you use the get accessor. It gives back the value related to the given index. For instance:

MyClass myObject = new MyClass();

int value = myObject[3]; // Retrieving value at index 3 using the get accessor

In the above code, myObject is an instance of the class MyClass with a defined indexer. The get accessor is invoked by indexing myObject with [3], and the value at index three is returned.

Modifying values using the Set Accessor

The set accessor can be used to change a value in an indexer. When you index an instance of a class or struct to assign a value to an element, you use the set accessor. It alters the value linked to the chosen index. For instance:

MyClass myObject = new MyClass();

myObject[3] = 10; // Setting value ten at index 3 using the set accessor

MyObject is an instance of MyClass with an indexer in the code above. MyObject is given an index of [3] and a value of 10, which calls the set accessor and updates the value at index 3.

Handling Potential Exceptions for Invalid Index or Null Reference:

It's critical to manage potential exceptions when working with indexers. An IndexOutOfRangeException will be issued if you attempt to access an index that is out of range or doesn't exist. Additionally, a NullReferenceException will be raised if you attempt to access an indexer on a null reference. To prevent unplanned programme crashes, it's imperative that your code gracefully handles these exceptions. Try-catch blocks can manage these exceptions, send the proper error messages, and take different actions when necessary.

Read-Only Indexers

In C#, read-only indexers do not permit modification but offer a mechanism to obtain values from a collection or class. They come in handy when you wish to grant read-only access to a class's elements or attributes without making it possible to modify them.

Implementing Read-Only Indexers: You can define an indexer without a set accessor to build a read-only indexer. This means that users of your class can only obtain values from the Indexer; they cannot add new entries to it. Using the supplied index, the get accessor is in charge of returning the desired value.

using System;

class MyCollection

{

    private int[] data = new int[5] { 1, 2, 3, 4, 5 };

    // Read-only indexer

    public int this[int index]

    {

        // Only the get accessor is defined

        get

        {

            if (index >= 0 && index < data.Length)

            {

                return data[index];

            }

            else

            {

                throw new IndexOutOfRangeException();

            }

        }

    }

}

class Program

{

    static void Main()

    {

        MyCollection collection = new MyCollection();

        // Accessing elements using the read-only Indexer

        Console.WriteLine(collection[0]);

        Console.WriteLine(collection[3]);

    }

}

Output:

C# Indexers

Benefits

Read-only indexers have a number of benefits and use cases, including:

  1. Data Encapsulation: By using read-only indexers, you can encapsulate the internal data of your class and grant regulated access to it. The integrity and consistency of the underlying data can be preserved by avoiding direct indexer manipulation.
  2. Security and data integrity: By limiting access to sensitive data, read-only indexers can improve the security of your class. It makes sure the data doesn't change and stops unauthorised alterations.
  3. Immutable Collections: In immutable collections, where the elements cannot be changed after initialisation, read-only indexers are frequently employed. This enables you to design collections that ensure the accuracy of the information they include.
  4. Providing Different Views or Subsets of Data: Read-only indexers can deliver several views or subsets of data from a more considerable collection or data structure. Users can access and retrieve Specific elements or characteristics without having the power to change them.
  5. Simplified API: By providing a read-only indexer, you can simplify the API of your class by giving users an easy way to retrieve data without disclosing pointless write functionality. Your code's usability and maintainability may both benefit from this.

Implicit vs Explicit Interface Implementation

Indexer Implicit Implementation: An indexer is implicitly implemented when defined and used within a class without explicitly declaring the interface it is a part of. It is how indexers are implemented by default in C#. When utilising an indexer that has been implicitly implemented, the interface name is not required. Instead, you use the Indexer directly, as if it were a part of the class. Implementing implicitly is simple and doesn't call for any additional syntax.

Indexer Explicit Implementation: An explicit implementation might resolve naming conflicts when numerous interfaces define indexers with the same signature. Because the compiler will need to know which indexer implementation to employ, this may result in naming conflicts. You can use explicit implementation to address the conflicts in these situations. You must also provide the indexer name when accessing an interface with explicit implementation.

Indexers in Collections

Using Indexers in the dictionary, list, and array classes:

For easy access to elements based on an index or key, indexers can be implemented in collection classes such as dictionaries, lists, and arrays. This makes it possible to engage with the collection's contents in a natural and expressive way.

For example, in a List<T> class, you can define an indexer that allows accessing elements by their index:

public class MyList<T>

{

    private List<T> items = new List<T>();

    public T this[int index]

    {

        get { return items[index]; }

        set { items[index] = value; }

    }

}

Here, the Indexer allows getting and setting elements of the list using the familiar bracket notation:

MyList<int> myList = new MyList<int>();

myList[0] = 10; // Set value at index 0

int value = myList[0]; // Get value at index 0

Similarly, in a Dictionary<TKey, TValue> class, you can define an indexer that allows accessing elements by their key:

public class MyDictionary<TKey, TValue>

{

    private Dictionary<TKey, TValue> items = new Dictionary<TKey, TValue>();

    public TValue this[TKey key]

    {

        get { return items[key]; }

        set { items[key] = value; }

    }

}

With this Indexer, you can retrieve and assign values using the key:

MyDictionary<string, int> myDictionary = new MyDictionary<string, int>();

myDictionary["key1"] = 10; // Set value with key "key1"

int value = myDictionary["key1"]; // Get value with key "key1"

Indexers in Custom Classes:

You can also construct indexers in custom classes to give class members specialised access depending on an index or key. Thanks to this feature, you can now treat instances of your class as if they were arrays or dictionaries.

Here is an example of a particular class with an indexer that enables index-based access to elements:

public class MyCustomClass

{

    private int[] values = new int[5];

    public int this[int index]

    {

        get { return values[index]; }

        set { values[index] = value; }

    }

}

Now you can use the custom class as if it were an array:

MyCustomClass myClass = new MyCustomClass();

myClass[0] = 10; // Set value at index 0

int value = myClass[0]; // Get value at index 0

Example 1: Indexer for a Custom Class

using System;

class Fruits

{

    private string[] elements = new string[5];

    public string this[int index]

    {

        get

        {

            return elements[index];

        }

        set

        {

            elements[index] = value;

        }

    }

}

class Program

{

    static void Main()

    {

        Fruits collection = new Fruits();

        collection[0] = "Apple";

        collection[1] = "Mango";

        collection[2] = "Orange";

        Console.WriteLine(collection[1]);

    }

}

Output:

C# Indexers

This example defines a custom class, Fruits, with an indexer. The Indexer allows us to access elements in the elements array using an index. We can set values by assigning them to the Indexer (collection[0] = "Apple") and retrieve values by accessing the Indexer (collection[1]).

Example 2: Indexer with Multiple Parameters

sing System;

class Matrix

{

    private int[,] matrix = new int[3, 3];

    public int this[int row, int column]

    {

        get

        {

            return matrix[row, column];

        }

        set

        {

            matrix[row, column] = value;

        }

    }

}

class Program

{

    static void Main()

    {

        Matrix matrix = new Matrix();

        matrix[0, 0] = 1;

        matrix[0, 1] = 2;

        matrix[1, 0] = 3;

        Console.WriteLine(matrix[0, 1]); 

    }

}

Output:

C# Indexers

In this example, we create a Matrix class with an indexer that takes two parameters: row and column. This allows us to access individual elements of a two-dimensional matrix using the indexer syntax (matrix[0, 1] = 2).