Khóa học lập trình C# nâng cao

Khóa học lập trình C# nâng cao

IList trong C# IList trong C# IList trong C# IList trong C# IList trong C# 0/5 (24 reviews)

IList trong C#

Đã đăng 2018-07-31 16:57:56 bởi Kteam
0 bình luận 2093 lượt xem
IList trong C# 0 /5 stars (0 reviews)
 

Dẫn nhập

Ở các bài học trước, chúng ta đã cùng nhau tìm hiểu về ICOLLECTION TRONG C#. Hôm nay chúng ta sẽ cùng tìm hiểu về IList trong C#.


Nội dung

Để đọc hiểu bài này tốt nhất các bạn nên có kiến thức cơ bản về các phần:

Trong bài học này, chúng ta sẽ cùng tìm hiểu các vấn đề:

  • IList là gì?
  • Thực thi interface IList.

IList là gì?

IList là một interface trong bộ các interface được định nghĩa sẵn của .NET Framework.

Về khái niệm interface cũng như cách sử dụng interface các bạn có thể xem lại bài INTERFACE TRONG C#. Còn trong bài này mình chỉ tập trung vào thực thi IList.

Nếu như ICollection là 1 interface thể hiện tính chất của một collection thì IList là một interface đại diện cho 1 danh sách các đối tượng không cùng kiểu và các phần tử có thể được truy cập thông qua chỉ số phần tử”.  Hay nói ngắn gọn đây là một interface thể hiện tính chất của một list.

Interface IList yêu cầu chúng ta thực thi các Properties, phương thức và Indexer sau:

  • IsFixedSize: Trả về giá trị true hoặc false chỉ ra danh sách này có kích thước cố định hay không (danh sách có kích thước cố định là danh sách không được phép thêm hoặc xóa phần tử sau khi danh sách được khởi tạo nhưng vẫn có thể sửa giá trị các phần tử bên trong).
  • IsReadOnly: Trả về giá trị true hoặc false chỉ ra danh sách này có được thay đổi dữ liệu hay không (danh sách chỉ đọc là danh sách không được phép thêm, xóa, sửa các phần tử bên trong sau khi danh sách được khởi tạo).
  • Item[Int32]: Đây là một indexer giúp lấy hoặc gán giá trị cho một phần tử tại một vị trí được truyền vào (chi tiết sẽ trình bày trong phần ví dụ).
  • Add(Object): Phương thức thực hiện thêm một đối tượng vào trong danh sách. Phương thức này trả về một số nguyên là chỉ số phần tử của phần tử vừa thêm. Nếu thêm không thành công sẽ trả về -1.
  • Clear(): Phương thức thực hiện loại bỏ toàn bộ các phần tử ra khỏi danh sách.
  • Contains(Object): Phương thức trả về true hoặc false xác định có tồn tại 1 đối tượng trong danh sách hay không.
  • IndexOf(Object): Phương thức trả về một số nguyên xác định chỉ số phần tử của một đối tượng. Nếu đối tượng đó không tồn tại sẽ trả về -1.
  • Insert(Int32, Object): Phương thức thực hiện chèn một đối tượng vào danh sách tại vị trí được truyền vào.
  • Remove(Object): Phương thức thực hiện loại bỏ một phần tử ra khỏi danh sách. Nếu phần tử truyền vào không tồn tại sẽ không báo lỗi và danh sách vẫn giữ nguyên.
  • RemoveAt(Int32): Phương thức thực hiện loại bỏ một phần tử tại vị trí truyền vào ra khỏi danh sách.  Nếu vị trí truyền vào không hợp lệ (nằm ngoài phạm vi của danh sách) sẽ thông báo lỗi.

Lưu ý: interface IList được kế thừa từ hai interface khác là ICollection IEnumerable. Vì thế khi thực thi hai interface này chúng ta cũng phải thực thi luôn các phương thức, properties trong các interface trên. Chi tiết về interface ICollection đã được trình bày trong bài ICOLLECTION TRONG C#IEnumerable sẽ được trình bày trong bài IENUMERABLE TRONG C#.


Thực thi interface IList

Chúng ta sẽ cùng tiếp tục tổ chức một collection đơn giản tiếp nối ví dụ trước. Hôm nay chúng ta sẽ cùng thực thi interface IList.

Trước khi vào phần chính mình xin phép sửa đổi lại chương trình cũ một chút để có thể giống ArrayList hơn.

Những sửa đổi bao gồm:

  • Thêm thuộc tính capacity nhằm lưu trữ sức chứa của MyArrayList và thuộc tính này sẽ có cơ chế hoạt động như thuộc tính Capacity trong ArrayList (Chi tiết mình đã trình bày trong bài ARRAYLIST TRONG C#).
private int capacity; // sức chứa của danh sách
public int Capacity
{
      get { return capacity; }
      set { capacity = value; }
}
  • Cập nhật lại phạm vi truy cập của property Count và bổ sung xử lý liên quan đến Capacity.
public int Count
{
get { return count; }

private set 
{ 
count = value;

if (count > Capacity)
{
Capacity = (Capacity == 0 ? 4 : Capacity * 2);
object[] lstNewObj = new object[Capacity];

this.CopyTo(lstNewObj, 0);
this.lstObj = lstNewObj;
}
}
}

Ở đây mình sẽ quan tâm khi số lượng phần tử hiện tại của mảng (giá trị thuộc tính count) lớn hơn sức chứa của nó thì mình thực hiện tăng sức chứa lên gấp đôi, cấp phát lại vùng nhớ cho mảng với sức chứa mới và chép toàn bộ dữ liệu cũ qua mảng mới đã cấp phát. Mặc định Capacity sẽ có giá trị 0 và khi có một phần tử được thêm vào thì Capacity sẽ tăng lên 4.

Lưu ý là những quy ước này không phải mình tự đặt ra mà đều dựa trên ArrayList.

Chúng ta sẽ đi qua chi tiết thực thi của từng thành phần trong IList:

  • IsFixedSizeIsReadOnly: giá trị 2 thuộc tính này phụ thuộc vào ngữ cảnh bạn dùng mà gán giá trị phù hợp (ví dụ như mảng cổ điển sẽ có kích thước cố định,…). Trong bài này mình sẽ gán là false hết vì danh sách mình đang tổ chức có khả năng mở rộng và có thể hiệu chỉnh được các phần tử bên trong.
public bool IsFixedSize
{
get { return false; }
}

public bool IsReadOnly
{
get { return false; }
}
  • Item[Int32]: Trước khi thực thi indexer thì mình xin giải thích đơn giản cho các bạn hiểu về indexer. Các bạn hãy quan sát hình dưới đây:

Câu hỏi đặt ra là tại sao ArrayList thì có thế truy xuất thông qua chỉ số index còn Stack thì không? (tạm thời mình sẽ bỏ qua tính chất của Stack, cứ xem Stack như là một lớp bình thường).

Câu trả lời là do trong lớp ArrayList có khai báo một indexer để cho phép truy xuất trong qua chỉ số index. Các bạn có thể xem hình sau:

Quay lại vấn đề chính. Theo quy ước của .NET thì 1 danh sách có tính chất như là 1 list thì phải cho phép truy xuất thông qua chỉ số index. Vì thế khi thực thi interface IList thì ta phải thực thi luôn indexer này.

public object this[int index]
{
   get
   {
      // nếu chỉ mục vượt quá phạm vi danh sách thì báo lỗi
      if (index < 0 || index >= Count) 
      {
       throw new IndexOutOfRangeException();
      }

      return this.lstObj[index];
   }
   set
   {
      // nếu danh sách là chỉ đọc thì không cho phép gán giá trị.
      if (IsReadOnly == true) 
      {
      throw new NotSupportedException();
      }
      else
      {
      this.lstObj[index] = value;
      }
   }
}
  • Add(Object):
public int Add(object value)
{
    // Nếu danh sách là chỉ đọc hoặc cố định thì không cho thêm.
    if (this.IsReadOnly == true || this.IsFixedSize == true)
    {
        return -1;
    }
    else
    {
        this.Count++; // tăng số lượng phần tử thêm 1.
        this.lstObj[Count - 1] = value; // gán giá trị mới vào.
 
        return this.Count - 1;
     }
}

Ở đây các bạn cần lưu ý là theo quy ước của interface IList thì hàm Add phải trả về một số nguyên là chỉ số của phần tử mới được thêm vào, nếu thêm không thành công sẽ trả về -1.

  • Clear(): Nội dung hàm Clear này có thể được viết theo nhiều cách khác nhau tùy theo ngữ cảnh của người viết. Mình đang mô phỏng lại ArrayList nên mình sẽ dựa trên mô tả của nó để thực thi. Đó là hàm Clear chỉ gán lại giá trị Count = -1 Capacity vẫn giữ nguyên.
/// <summary>
/// Theo góc nhìn từ bên ngoài lớp nhìn vào thì
/// Count = -1 đồng nghĩa với mảng rỗng.
/// </summary>
public void Clear()
{
Count = 0;
}
  • Contains(Object): Phương thức này thì chúng ta chỉ việc kiểm tra đối tượng truyền vào có tồn tại trong danh sách hay không.
public bool Contains(object value)
{
    bool IsFound = false;

    for (int i = 0; i < Count; i++)
    {
         if (this.lstObj[i].Equals(value))
         {
             IsFound = true;
             break;
         }
    }

   return IsFound;
}
  • IndexOf(Object):
public int IndexOf(object value)
{
    int index = -1;

    for (int i = 0; i < Count; i++)
    {
         if (this.lstObj[i] == value)
        {
           index = i;
           break;
        }
    }

    return index;
}

Ở đây các bạn lưu ý là nếu có nhiều giá trị trong danh sách thì chỉ trả về vị trí xuất hiện đầu tiên.

  • Insert(Int32, Object):
public void Insert(int index, object value)
{
     if (IsFixedSize == true || IsReadOnly == true)
     {
         throw new NotSupportedException();
     }

     if (index < 0 || index >= Count)
     {
         throw new IndexOutOfRangeException();
     }

     Count++;
     // Thực hiện dời các phần tử sang phải để dư 1 chỗ cho phần tử cần chèn
     for (int i = Count - 1; i > index; i--)
     {
          this.lstObj[i] = this.lstObj[i - 1];
     }

     this.lstObj[index] = value;
}

 Vì mình đang dùng mảng bình thường để lưu danh sách phần tử nên khi chèn mình sẽ thực hiện thuật toán như trên. Nếu bạn sử dụng cấu trúc dữ liệu khác thì bạn điều chỉnh cho phù hợp nhé!

  • RemoveAt(Int32):
public void RemoveAt(int index)
{
     if (index >= 0 && index < Count)
    {
       // dời sang trái các phần tử bên phải phần tử cần xóa.
       for (int i = index; i < Count - 1; i++)
       {
            this.lstObj[i] = this.lstObj[i + 1];
       }

       Count--;
    }
}

Tương tự phương thức Insert, nếu các bạn sử dụng cấu trúc dữ liệu khác thì bạn điều chỉnh cho phù hợp nhé!

  • Remove(Object): Mình sẽ tận dụng hai phương thức đã viết ở trên để thực thi phương thức này.
public void Remove(object value)
{
    RemoveAt(IndexOf(value));
}

Ở đây các bạn lưu ý nếu có nhiều phần tử có giá trị bằng value thì chỉ xóa phần tử đầu tiên. Do đó mình tận dụng phương thức IndexOf RemoveAt để viết cho nhanh.

Đến đây thì chúng ta đã hoàn thành xong phần thực thi interface IList và hoàn thành 70% MyArrayList.

Bây giờ các bạn có thể chạy thử, thêm xóa phần tử xem nó hoạt động có giống ArrayList không nhé!


Kết luận

Nội dung bài này giúp các bạn nắm được:

  • IList là gì?
  • Thực thi interface IList.

Bài học sau chúng ta sẽ cùng tìm hiểu về IENUMERATOR VÀ IENUMERABLE TRONG C#.

Cảm ơn các bạn đã theo dõi bài viết. Hãy để lại bình luận hoặc góp ý của mình để phát triển bài viết tốt hơn. Đừng quên “Luyện tập – Thử thách – Không ngại khó”.

 


Tài liệu 

Nhằm phục vụ mục đích học tập Offline của cộng đồng, Kteam hỗ trợ tính năng lưu trữ nội dung bài học IList trong C# dưới dạng file PDF trong link bên dưới.

Ngoài ra, bạn cũng có thể tìm thấy các tài liệu được đóng góp từ cộng đồng ở mục TÀI LIỆU trên thư viện Howkteam.com

Đừng quên like hoặc +1 Google để ủng hộ Kteam và tác giả nhé! 


Thảo luận

Nếu bạn có bất kỳ khó khăn hay thắc mắc gì về khóa học, đừng ngần ngại đặt câu hỏi trong phần BÌNH LUẬN bên dưới hoặc trong mục HỎI & ĐÁP trên thư viện Howkteam.com để nhận được sự hỗ trợ từ cộng đồng. 

 

Chia sẻ:
Thảo luận Hỏi và đáp Báo lỗi bài viết
Hủy bỏ   hoặc  
Hủy bỏ   hoặc  
Hủy bỏ   hoặc  

Chiến dịch

Kteam - Howkteam Free Education