Toán tử chuyển đổi

c-sharp

(Phạm Quyết Nghị) #1

C# cho phép chuyển đổi từ kiểu int sang kiểu long một cách ngầm định, và cũng cho phép chúng ta chuyển từ kiểu long sang kiểu int một cách tường minh. Việc chuyển từ kiểu int sang kiểu long được thực hiện ngầm định bởi vì hiển nhiên bất kỳ giá trị nào của int cũng được thích hợp với kích thước của kiểu long. Tuy nhiên, điều ngược lại, tức là chuyển từ kiểu long sang kiểu int phải được thực hiện một cách tường minh (sử dụng ép kiểu) bởi vì ta có thể mất thông tin khi giá trị của biến kiểu long vượt quá kích thước của int lưu trong bộ nhớ:

int myInt = 5;
long myLong;
myLong = myInt; // ngầm định
myInt = (int) myLong; // tường minh

Chúng ta muốn thực hiện việc chuyển đổi này với lớp Fraction. Khi đưa ra một số nguyên, chúng ta có thể hỗ trợ ngầm định để chuyển đổi thành một phân số bởi vì bất kỳ giá trị nguyên nào ta cũng có thể chuyển thành giá trị phân số với mẫu số là 1 như (24 == 24/1).

Khi đưa ra một phân số, chúng ta muốn cung cấp một sự chuyển đổi tường minh trở lại một số nguyên, điều này có thể hiểu là một số thông tin sẽ bị mất. Do đó, khi chúng ta chuyển phân số 9/4 thành giá trị nguyên là 2.

Từ ngữ ngầm định (implicit) được sử dụng khi một chuyển đổi đảm thành công mà không mất bất cứ thông tin nào của dữ liệu nguyên thủy. Trường hợp ngược lại, tường minh (explicit) không đảm bảo bảo toàn dữ liệu sau khi chuyển đổi do đó việc này sẽ được thực hiện một cách công khai.

Ví dụ 6.1 sẽ trình bày dưới đây minh họa cách thức mà chúng ta có thể thực thi chuyển đổi tường minh và ngầm định, và thực thi một vài các toán tử của lớp Fraction. Trong ví dụ này chúng ta sử dụng hàm Console.WriteLine() để xuất thông điệp ra màn hình minh họa khi phương thức được thi hành. Tuy nhiên cách tốt nhất là chúng ta sử dụng trình bebug để theo dõi từng bước thực thi các lệnh hay nhảy vào từng phương thức được gọi.

– Ví dụ 6.1: Định nghĩa các chuyển đổi và toán tử cho lớp Fraction.

using System;
public class Fraction
{
	public Fraction(int numerator,int denominator)
	{
		Console.WriteLine("In Fraction Constructor( int, int) ");
		this.numerator = numerator;
		this.denominator = denominator;
	}
	
	public Fraction(int wholeNumber)
	{
		Console.WriLine("In Fraction Constructor( int )");
		numerator = wholeNumber;
		denominator = 1;
	}
	
	public static implicit operator Fraction( int theInt )
	{
		Console.WriteLine(" In implicit conversion to Fraction");
		return new Fraction( theInt );
	}
	
	public static explicit operator int( Fraction theFraction )
	{
		Console.WriteLine("In explicit conversion to int");
		return theFraction.numerator / theFraction.denominator;
	}
	
	public static bool operator == ( Fraction lhs, Fraction rhs)
	{
		Console.WriteLine("In operator ==");
		if ( lhs.numerator == rhs.numerator &&
		lhs.denominator == rhs.denominator )
		{
			return true;
		}
		
		// thực hiện khi hai phân số không bằng nhau
		return false;
	}
	
	public static bool operator != ( Fraction lhs, Fraction rhs)
	{
		Console.WriteLine("In operator !=");
		return !( lhs == rhs );
	}
	
	public override bool Equals( object o )
	{
		Console.WriteLine("In method Equals");
		
		if ( !(o is Fraction ))
		{
			return false;
		}
		
		return this == ( Fraction ) o;
	}

	public static Fraction operator+( Fraction lhs, Fraction rhs )
	{
		Console.WriteLine("In operator +");
		
		if (lhs.denominator == rhs.denominator )
		{
			return new Fraction( lhs.numerator + rhs.numerator, lhs.denominator );
		}
		
		//thực hiện khi hai mẫu số khộng bằng nhau
		int firstProduct = lhs.numerator * rhs.denominator;
		int secondProduct = rhs.numerator * lhs.denominator;
		return new Fraction( firstProduct + secondProduct,
		lhs.denominator * rhs.denominator);
	}
	
	public override string ToString()
	{
		string s = numerator.ToString() + "/" + denominator.ToString();
		return s;
	}
	
	//biến thành viên lưu tử số và mẫu số
	private int numerator;
	private int denominator;
}

public class Tester
{
	static void Main()
	{
		Fraction f1 = new Fraction( 3, 4);
		Console.WriteLine("f1:{0}",f1.ToString());
		Fraction f2 = new Fraction( 2, 4);
		Console.WriteLine("f2:{0}",f2.ToString());
		Fraction f3 = f1 + f2;
		Console.WriteLine("f1 + f2 = f3:{0}",f3.ToString());
		Fraction f4 = f3 + 5;
		Console.WriteLine("f4 = f3 + 5:{0}",f4.ToString());
		Fraction f5 = new Fraction( 2, 4);
		
		if( f5 == f2 )
		{
			Console.WriteLine("f5:{0}==f2:{1}",
			f5.ToString(), f2.ToString());
		}
	}
}

Lớp Fraction bắt đầu với hai hàm khởi dựng: một hàm lấy một tử số và mẫu số, còn hàm kia lấy chỉ lấy một số làm tử số. Tiếp sau hai bộ khởi dựng là hai toán tử chuyển đổi. Toán tử chuyển đổi đầu tiên chuyển một số nguyên sang một phân số:

public static implicit operator Fraction( int theInt )
{
	return new Fraction( theInt);
}

Sự chuyển đổi này được thực hiện một cách ngầm định bởi vì bất cứ số nguyên nào cũng có thể được chuyển thành một phân số bằng cách thiết lập tử số bằng giá trị số nguyên và mẫu số có giá trị là 1. Việc thực hiện này có thể giao lại cho phương thức khởi dựng lấy một tham số.

Toán tử chuyển đổi thứ hai được thực hiện một cách tường minh, chuyển từ một Fraction ra một số nguyên:

public static explicit operator int( Fraction theFraction )
{
	return theFraction.numerator / theFraction.denominator;
}

Bởi vì trong ví dụ này sử dụng phép chia nguyên, phép chia này sẽ cắt bỏ phần phân chỉ lấy phần nguyên. Do vậy nếu phân số có giá trị là 16/15 thì kết quả số nguyên trả về là 1. Một số các phép chuyển đổi tốt hơn bằng cách sử dụng làm tròn số.

Tiếp theo sau là toán tử so sánh bằng (==) và toán tử so sánh không bằng (!=). Chúng ta nên nhớ rằng khi thực thi toán tử so sánh bằng thì cũng phải thực thi toán tử so sánh không bằng.

Chúng ta đã định nghĩa giá trị bằng nhau giữa hai Fraction khi tử số bằng tử số và mẫu số bằng mẫu số. Vi dụ, như hai phân số 3/4 và 6/8 thì không được so sánh là bằng nhau. Một lần nữa, một sự thực thi tốt hơn là tối giản tử số và mẫu số khi đó 6/8 sẽ đơn giản thành 3/4 và khi đó so sánh hai phân số sẽ bằng nhau.

Trong lớp này chúng ta cũng thực thi phủ quyết phương thức Equals() của lớp object, do đó đối tượng Fraction của chúng ta có thể được đối xử một cách đa hình với bất cứ đối tượng khác. Trong phần thực thi của phương thức chúng ta ủy thác việc so sánh lại cho toán tử so sánh bằng cách gọi toán tử (==).

Lớp Fraction có thể thực thi hết tất cả các toán tử số học như cộng, trừ, nhân, chia. Tuy nhiên, trong phạm vi nhỏ hẹp của minh họa chúng ta chỉ thực thi toán tử cộng, và thậm chí phép cộng ở đây được thực hiện đơn giản nhất. Chúng ta thử nhìn lại, nếu hai mẫu số bằng nhau thì ta cộng tử số:

public static Fraction operator + ( Fraction lhs, Fraction rhs)
{
	if ( lhs.denominator == rhs.denominator)
	{
		return new Fraction( lhs.numerator + rhs.numerator, lhs.denominator);
	}
}

Nếu mẫu số không cùng nhau, thì chúng ta thực hiện nhân chéo:

int firstProduct = lhs.numerator * rhs.denominator;
int secondProduct = rhs.numerator * lhs.denominator;
return new Fraction( firstProduct + secondProduct, lhs.denominator * rhs.denominator);

Cuối cùng là sự phủ quyết phương thức ToString() của lớp object, phương thức mới này thực
hiện viết xuất ra nội dung của phân số dưới dạng : tử số / mẫu số:

public override string ToString()
{
	string s = numerator.ToString() + “/” + denominator.ToString();
	return s;
}

Chúng ta tạo một chuỗi mới bằng cách gọi phương thức ToString() của numerator. Do numerator là một đối tượng, nên trình biên dịch sẽ ngầm định thực hiện boxing số nguyên numerator và sau đó gọi phương thức ToString(), trả về một chuỗi thể hiện giá trị của số nguyên numerator. Sau đó ta nối chuỗi với “/” và cuối cùng là chuỗi thể hiện giá trị của mẫu số.

Với lớp Fraction đã tạo ra, chúng ta thực hiện kiểm tra lớp này. Đầu tiên chúng ta tạo ra hai
phân số 3/4, và 2/4:

Fraction f1 = new Fraction( 3, 4);
Console.WriteLine("f1:{0}",f1.ToString());
Fraction f2 = new Fraction( 2, 4);
Console.WriteLine("f2:{0}",f2.ToString());

Kết quả thực hiện các lệnh trên như sau:

In Fraction Constructor(int, int)
f1: 3/4
In Fraction Constructor(int, int)
f2: 2/4

Do trong phương phức khởi dựng của lớp Fraction chúng ta có gọi hàm WriteLine() để xuất ra thông tin bộ khởi dựng nên khi tạo đối tượng (new) thì cũng các thông tin này sẽ được hịển thị.

Dòng tiếp theo trong hàm Main() sẽ gọi toán tử cộng, đây là phương thức tĩnh. Mục đích của toán tử này là cộng hai phân số và trả về một phân số mới là tổng của hai phân số đưa vào:

Fraction f3 = f1 + f2;
Console.WriteLine(“f1 + f2 = f3: {0}”, f3.ToString());

Hai câu lệnh trên sẽ cho ra kết quả như sau:

In operator +
In Fraction Constructor( int, int)
f1 + f2 = f3: 5/4

Toán tử + được gọi trước sau đó đến phương thức khởi dựng của đối tượng f3. Phương thức khởi dựng này lấy hai tham số nguyên để tạo tử số và mẫu số của phân số mới f3.

Hai câu lệnh tiếp theo cộng một giá trị nguyên vào phân số f3 và gán kết quả mới về cho phân số mới f4:

Fraction f4 = f3 + 5;
Console.WriteLine(“f3 + 5 = f4: {0}”, f4.ToString());

Kết quả được trình bày theo thứ tự sau:

In implicit conversion to Fraction
In Fraction Construction(int)
In operator+
In Fraction Constructor(int, int)
f3 + 5 = f4: 25/4

Ghi chú: rằng toán tử chuyển đổi ngầm định được gọi khi chuyển 5 thành một phân số. Phân số được tạo ra từ toán tử chuyển đổi ngầm định này gọi phương thức khởi dựng một tham số để tạo phân số mới 5/1. Phân số mới này sẽ được chuyển thành toán hạng trong phép cộng với phân số f3 và kết quả trả về là phân số f4 là tổng của hai phân số trên.

Thử nghiệm cuối cùng là tạo một phân số mới f5, rồi sau đó gọi toán tử nạp chồng so sánh bằng để kiểm tra xem hai phân số có bằng nhau hay không.


Tài liệu c# tiếng việt cho người mới bắt đầu (newbie)

89% Thành viên có cách giải cho một câu hỏi. Bạn thì sao?