Indexer trong C#

Indexer (bộ lập chỉ mục) là một loại thuộc tính đặc biệt cho phép một lớp hoặc struct được truy cập như một mảng.

Một bộ lập chỉ mục có thể được định nghĩa giống như thuộc tính với từ khóa this và dấu ngoặc vuông [].

Cú pháp

Một bộ lập chỉ mục một chiều có cú pháp sau:

element-type this[int index] 
{
   // The get accessor.
   get 
   {
      // return the value specified by index
   }
   
   // The set accessor.
   set 
   {
      // set the value specified by index
   }
}

Sử dụng bộ lập chỉ mục

Khai báo một bộ lập chỉ mục ở một mức độ nào đó tương tự như một thuộc tính. Bạn sử dụng các accessor là getset định nghĩa một bộ lập chỉ mục.

Tuy nhiên, các thuộc tính trả về hoặc gán giá trị cho một thành viên dữ liệu cụ thể, trong khi đó bộ lập chỉ mục trả về hoặc gán một giá trị cụ thể từ đối tượng.

Nói cách khác, nó chia dữ liệu của thể hiện đối tượng thành các phần nhỏ hơn và lập chỉ mục cho từng phần, lấy hoặc gán giá trị cho từng phần này.

Định nghĩa một thuộc tính phải có tên thuộc tính. Bộ lập chỉ mục không được định nghĩa bằng tên, nhưng nó sử dụng từ khóa this, dùng để chỉ thể hiện đối tượng hiện tại.

Ví dụ sau định nghĩa một bộ lập chỉ mục trong lớp.

using System;

namespace IndexerApplication 
{   
   class IndexedNames 
   {
      private string[] namelist = new string[size];
      public static int size = 10;
      
      public IndexedNames() 
      {
         for (int i = 0; i < size; i++)
         {
            namelist[i] = "N. A.";
         }
      }
      
      public string this[int index] 
      {
         get 
         {
            string tmp;
         
            if(index >= 0 && index <= size-1) 
            {
               tmp = namelist[index];
            } 
            else 
            {
               tmp = "";
            }
            
            return tmp;
         }
         set 
         {
            if( index >= 0 && index <= size-1 ) 
            {
               namelist[index] = value;
            }
         }
      }
      
      static void Main(string[] args) 
      {
         IndexedNames names = new IndexedNames();
         names[0] = "Zara";
         names[1] = "Riz";
         names[2] = "Nuha";
         names[3] = "Asif";
         names[4] = "Davinder";
         names[5] = "Sunil";
         names[6] = "Rubic";
         
         for (int i = 0; i < IndexedNames.size; i++) 
         {
            Console.WriteLine(names[i]);
         }
         Console.ReadKey();
      }
   }
}

Kết quả khi biên dịch và chạy chương trình:

Zara
Riz
Nuha
Asif
Davinder
Sunil
Rubic
N. A.
N. A.
N. A.

Lớp IndexedNames định nghĩa một bộ lập chỉ mục cho mảng riêng của nó là namelist. Vì vậy, bây giờ, bạn có thể sử dụng IndexedNames giống như một mảng để thêm và truy xuất các giá trị chuỗi từ mảng namelist.

Bạn có thể sử dụng cú pháp biểu thức thân để get và set từ C # 7 trở đi như sau:

class StringDataStore
{
    private string[] strArr = new string[10]; // internal data storage

    public string this[int index]
    {
        get => strArr[index];

        set => strArr[index] = value;
    }
}

Bộ lập chỉ mục chung (generic indexer)

Bộ lập chỉ mục cũng có thể chung chung. Lớp chung (generic class) sau đây trình bày về bộ lập chỉ mục chung.

using System;

class DataStore<T>
{
	private T[] store;
	public DataStore()
	{
		store = new T[10];
	}

	public DataStore(int length)
	{
		store = new T[length];
	}

	public T this[int index]
	{
		get
		{
			if (index < 0 && index >= store.Length)
            {
				throw new IndexOutOfRangeException("Index out of range");
            }
			return store[index];
		}
		set
		{
			if (index < 0 || index >= store.Length)
            {
				throw new IndexOutOfRangeException("Index out of range");
			}
            store[index] = value;
		}
	}

	public int Length
	{
		get
		{
			return store.Length;
		}
	}
}

public class Program
{
	public static void Main()
	{
		DataStore<int> grades = new DataStore<int>();
		grades[0] = 100;
		grades[1] = 25;
		grades[2] = 34;
		grades[3] = 42;
		grades[4] = 12;
		grades[5] = 18;
		grades[6] = 2;
		grades[7] = 95;
		grades[8] = 75;
		grades[9] = 53;
		
		for (int i = 0; i < grades.Length; i++)
        {
			Console.WriteLine(grades[i]);
        }
		
		DataStore<string> names = new DataStore<string>(5);
		names[0] = "Steve";
		names[1] = "Bill";
		names[2] = "James";
		names[3] = "Ram";
		names[4] = "Andy";
		
		for (int i = 0; i < names.Length; i++)
        {
			Console.WriteLine(names[i]);
        }
	}
}

Đây là kết quả khi biên dịch và chạy chương trình:

100
25
34
42
12
18
2
95
75
53
Steve
Bill
James
Ram
Andy

Bộ lập chỉ mục chung ở trên có thể được sử dụng với bất kỳ kiểu dữ liệu nào. Ví dụ sau đây cho thấy việc sử dụng bộ chỉ mục chung.

Nạp chồng bộ lập chỉ mục

Bạn có thể nạp chồng với các loại dữ liệu khác nhau cho chỉ mục. Ví dụ sau đây nạp chồng một bộ lập chỉ mục với chỉ mục kiểu int cũng như chỉ mục kiểu chuỗi.

class StringDataStore
{
    private string[] strArr = new string[10]; // internal data storage

    // int type indexer
    public string this[int index]
    {
        get
        {
            if (index < 0 &&  index >= strArr.Length)
            {
                throw new IndexOutOfRangeException("Index out of range");
			}
            return strArr[index];
        }
        set
        {
            if (index < 0 &&  index >= strArr.Length)
            {
                throw new IndexOutOfRangeException("Index out of range");
			}
            strArr[index] = value;
        }
    }

    // string type indexer
    public string this[string name]
    {
        get
        {
            foreach (string str in strArr)
            {
                if(str.ToLower() == name.ToLower())
                {
                    return str;
                }
            }                    
            return null;
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        StringDataStore strStore = new StringDataStore();

        strStore[0] = "One";
        strStore[1] = "Two";
        strStore[2] = "Three";
        strStore[3] = "Four";
        
        Console.WriteLine(strStore["one"]);
        Console.WriteLine(strStore["two"]);
        Console.WriteLine(strStore["Three"]);
        Console.WriteLine(strStore["Four"]);
    }
}

Đây là kết quả khi biên dịch và chạy chương trình trên:

One
Two
Three
Four

Tips: Indexer không cho phép tham số ref và out.
Lập Trình C#Lập Trình C# Cơ Bản
Bài Viết Liên Quan:
int[] và int[,] trong C#: Ai nhanh hơn
Trung Nguyen 10/10/2020
int[] và int[,] trong C#: Ai nhanh hơn

Hiểu được sự khác biệt giữa các loại mảng trong C# sẽ giúp bạn chọn cấu trúc dữ liệu chính xác cho mọi trường hợp.

Struct và class trong C#: Ai nhanh hơn
Trung Nguyen 09/10/2020
Struct và class trong C#: Ai nhanh hơn

Trong bài viết này, tôi sẽ so sánh sự khác biệt về hiệu suất giữa struct và class trong C#: Ai nhanh hơn.

Best practice cho performance trong C#
Trung Nguyen 03/10/2020
Best practice cho performance trong C#

Mục tiêu của bài viết này là cung cấp một danh sách không đầy đủ các code mẫu cần tránh, vì chúng rủi ro hoặc performance kém.

Đọc ghi file (File I/O) trong C#
Trung Nguyen 26/04/2020
Đọc ghi file (File I/O) trong C#

Hướng dẫn này sẽ giúp bạn tìm hiểu về đọc ghi file (File I/O) trong C# và sử dụng các lớp tiện ích để đọc ghi file.