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 [].
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
}
}
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à get và set đị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 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.
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.
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.
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.
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.
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.