Chương 25: Làm quen với
.NET Frameworks
Tác giả: Sưu tầm
Khái quát
Thông tin trong các chương trước là đủ để tạo các đối tượng sẽ hoạt động trong .NET Runtime, nhưng các đối tượng đó sẽ không giống như chúng được tạo để hoạt động tốt trong cơ cấu. Chương này sẽ trình bày chi tiết cách tạo các đối tượng người sử dụng định nghĩa thực thi giống các đối tượng trong thời gian chạy .NET và Frameworks.
Những thứ mà tất cả các đối tượng sẽ thực hiện
Việc chồng hàm ToString() từ lớp object mang lại một sự thể hiện tốt của các giá trị trong một đối tượng. Nếu điều này không được thực hiện, object.ToString() sẽ đơn giãn trả ra tên của lớp đó.
Hàm Equals() trong object được gọi bởi các lớp .NET Frameworks để xác định hai đối tượng có bằng nhau không.
Một lớp cũng có thể chồng operator==() và operator!=(), để cho phép người sử dụng sử dụng các toán tử dựng sẵn với các thể hiện của đối tượng, thay vì gọi Equals().
ToString()
Sau đây là một ví dụ trình bày những gì xảy ra theo mặc định:
using System;
public class Employee {
public Employee(int id, string name) {
this.id = id;
this.name = name;
}
int id;
string name;
}
class Test {
public static void Main() {
Employee herb = new Employee(555, “Herb”);
Console.WriteLine(“Employee: {0}”, herb);
}
}
Mã trên sẽ trả ra kết quả sau:
Employee: Employee
Bằng cách chồng hàm ToString(), một biểu diễn có thể có nhiều hữu ích hơn:
using System;
public class Employee {
public Employee(int id, string name) {
this.id = id;
this.name = name;
}
return(String.format(“{0}{1})”, name, id));
{
int id;
string name;
}
class Test {
public static void Main() {
Employee herb = new Employee(555, “Herb”);
Console.WriteLine(“Employee: {0}”, herb);
}
}
Nó mang lại một kết quả tốt hơn nhiều:
Employee: Herb(555)
Khi Console.WriteLine() cần thiết chuyển đổi một đối tượng thành một chuỗi, nó sẽ gọi hàm ảo ToString(), điều này dẫn đến một cài đặt đặc biệt của đối tượng. Nếu nhiều điều khiển được định dạng là được dẫn xuất, ví dụ như cài đặt một lớp điều khiển thực với các định dạng khác nhau, giao diện IFormatable có thể được chồng. IFormatable được trnìh bày trong mục “Tuỳ chọn giao diện đối tượng” trong Chương 30, “Khái quát .NET Frameworks”.
Equals()
Equals() được dùng để xác định hai đối tượng là có các nội dung như nhau hay không. Hàm này được gọi bởi các lớp sưu tập (như Array hay Hashtable) để xác định hai đối tượng là có bằng nhau không. Mở rộng ví dụ employee:
using System;
public class Employee {
public Employee(int id, string name) {
this.id = id;
this.name = name;
}
public override string ToString() {
return(name + “(” + id + “)”);
}
public override bool Equals(object obj) {
Employee emp2 = (Employee) obj;
if (id != emp2.id)
return(false);
if (name != emp2.name)
return(false);
return(true);
}
public static bool operator==(Employee emp1, Employee emp2) {
return(emp1.Equals(emp2));
}
public static bool operator!=(Employee emp1, Employee emp2) {
return(!emp1.Equals(emp2));
}
int id;
string name;
}
class Test {
public static void Main() {
Employee herb = new Employee(555, “Herb”);
Employee herbClone = new Employee(555, “Herb”);
Console.WriteLine(“Equal: {0}”, herb.Equals(herbClone));
Console.WriteLine(“Equal: {0}”, herb == h rbClone);
}
}
Chương trình này cho ra kết quả sau:
Equal: true
Equal: true
Trong trường hợp này, operator==() và operator!=() cũng được chồng, nó cho phép cú pháp toán tử được sử dụng trong dòng cuối của hàm Main(). Các toán tử này phải được chồng cùng nhau; chúng không thể được chồng tách rời.
Hàm băm và GetHashCode()
Frameworks bao gồm lớp Hashtable, mà nó là rất hữu ích để tìm kiếm nhanh các đối tượng thông qua một khoá. Bảng băm làm việc bằng cách sử dụng một hàm băm, nó sinh ra một khoá kiểu nguyên cho một thể hiện cụ thể của một lớp. Khoá này sẽ là phiên bản súc tích các nội dung của thể hiện. Trong khi các thể hiện có thể có cùng mã băm, điều này rõ ràng là không chắc xảy ra.
Bẳng băm sử dụng khoá này như một cách của việc giới hạn quyết liệt số lượng các đối tượng phải được duyệt để tìm một đối tượng nhất định trong sưu tập các đối tượng. Nó thực hiện điều này trước tiên bằng cách nhận một giá trị băm của đối tượng, mà nó sẽ loại trừ tất cả các đối tượng với một mã băm khác, chỉ để lại những cái có cùng mã băm để tìm kiếm. Vì số lượng các thể hiện với mã băm đó là nhỏ, nên việc tìm kiếm có thể là nhanh hơn nhiều.
Đó là ý tưởng cơ bản – để giải thích chi tiết hơn, xin tham khảo các sách cấu trúc dữ liệu và thuật toán tốt. Hàm băm là một cấu trúc vô cùng hữu dụng. Lớp Hashtable lưu trữ các đối tượng, nên nó đơn giản sử dụng chúng để lưu trữ bất cứ kiểu gì.
Hàm GetHashCode() nên được chồng trong các lớp do người sử dụng tạo ra bởi vì các giá trị được trả ra từ GetHashCode() đòi hỏi phải liên quan đến giá trị trả ra từ Equals(). Hai đối tượng là giống nhau qua Equals() phải luôn luôn trả ra cùng mã băm.
Cài đặt mặc định của GetHashCode() không làm việc theo cách này, và do đó nó phải được chồng để làm việc chính xác. Nếu không được chồng, mã băm sẽ chỉ là đồng nhất với cùng thể hiện của một đối tượng, và việc tìm kiếm một đối tượng mà là bằng nhưng không cùng thể hiện sẽ lỗi.
Nếu có một trường duy nhất trong một đối tượng, có lẽ là lựa chọn tốt cho mã băm:
using System;
using System.Collections;
public class Employee {
public Employee(int id, string name) {
this.id = id;
this.name = name;
}
public override string ToString() {
return(String.Format(“{0}({1})”, na me, id));
}
public override bool Equals(object obj) {
Employee emp2 = (Employee) obj;
if (id != emp2.id)
return(false);
if (name != emp2.name)
return(false);
return(true);
}
public static bool operator==(Employee emp1, Employee emp2) {
return(emp1.Equals(emp2));
}
public static bool operator!=(Employee emp1, Employee emp2) {
return(!emp1.Equals(emp2));
}
public override int GetHashCode() {
return(id);
}
int id;
string name;
}
class Test {
public static void Main() {
Employee herb = new Employee(555, “Herb”);
Employee george = new Employee(123, “George”);
Employee frank = new Employee(111, “Frank”);
Hashtable employees = new Hashtable();
employees.Add(herb, “414 Evergreen Terrace”);
employees.Add(george, “2335 Elm Street“);
employees.Add(frank, “18 Pine Bluff Road“);
Employee herbClone = new Employee(555, “Herb”);
string address = (string) employees[herbClone];
Console.WriteLine(“{0} lives at {1}”, herbClone, address);
}
}
Trong lớp Employee, thành phần id là duy nhất, nên nó được sử dụng cho mã băm. Trong hàm Main(), vài employee được tạo, và chúng sau đó được sử dụng như các giá trị khoá để lưu trữ địa chỉ của các employee.
Nếu không có một trường duy nhất, mã băm nên được tạo ngoài các giá trị được chứa trong một hàm. Nếu lớp Employee không có một định danh duy nhất, nhưng có các trường tên và địa chỉ, hàm băm có thể được sử dụng chúng. Chương trình sau trình bày một hàm băm có thể được sử dụng:
using System;
using System.Collections;
public class Employee {
public Employee(string name, string address) {
this.name = name;
this.address = address;
}
public override int GetHashCode() {
return(name.GetHashCode() + address.GetHashCode());
}
string name;
string address;
}
Cài đặt này của GetHashCode() đơn giản thêm các mã băm của các thành phần với nhau, và trả ra chúng.