MSIL cơ bản – Part 5/6: Conversion, casting và boxing

Việc chuyển đổi một giá trị từ kiểu có phạm vi nhỏ sang kiểu có phạm vi lớn hơn không làm mất mát dữ liệu vì thế chúng được thực hiện một cách ngầm định. Ngược lại, việc chuyển đổi từ một kiểu dữ liệu có phạm vi lớn sang kiểu có phạm vi nhỏ hơn có thể làm dữ liệu bị cắt bớt nếu như độ lớn của nó vượt quá phạm vi của kiểu dữ liệu đích.

Lệnh conv.xx

conv.xx bao gồm nhiều lệnh tùy theo kiểu dữ liệu đích, trong đó là xx là kiểu dữ liệu đích. Ngoài ra còn có thể dùng conv.ovf.xx để ném ra ngoại lệ nếu như việc tràn đổi gây ra tràn số.

Lệnh conv không yêu cầu tham số, dữ liệu cần chuyển đổi sẽ được đưa vào stack trước khi gọi lệnh. Sau khi thực hiện chuyển đổi, dữ liệu mới lại được đưa vào stack. Nếu dữ liệu nguồn và đích có cùng kiểu dữ liệu, lệnh conv sẽ không thực hiện gì cả.

• conv.i1: chuyển về kiểu int8

• conv.i2: chuyển về kiểu int16

• conv.i4: chuyển về kiểu int32

• conv.i8: chuyển về kiểu int64

Bạn có thể xem tại đây để tra cứu các kiểu chuyển đổi khác.

Dưới đây là một ví dụ chuyển một biến kiểu int16 (giá trị từ -32768 đến 32767) thành kiểu int8 (giá trị từ -128 đến 127).

.assembly extern mscorlib {}
.assembly Conversion{}

.method public static void  Main()
{
	.entrypoint
	.maxstack 2
	.locals init (int16 mshort, int8 msbyte)

	ldc.i4 200
	stloc mshort

	ldloc mshort
	conv.i1
	stloc msbyte

	ldloc msbyte

	call void [mscorlib]System.Console::WriteLine(int32)

	ret
}

Đầu tiên ta gán giá trị 200 cho biến mshort (kiểu int16), sau đó chuyển đổi sang kiểu int8 bằng lệnh conv.i1 và lưu vào biến msbyte. Khi in giá trị của biến msbyte ra thì kết quả nhận được không phải là 200 mà là -56. Nguyên nhân là khi chuyển đổi từ int16 sang int8, dữ liệu đã bị cắt bớt.

Lệnh isinst

Đây là lệnh dùng để kiểm tra một đối tượng kiểu tham chiếu có phải là một instance không. Sau khi kiểm tra lệnh này sẽ trả về một giá trị null hoặc instance của đối tượng đó. Trong C#, lệnh này tương đương với toán tử as.

Sau khi dùng lệnh này, ta thường sử dụng lệnh ldnull và dùng lệnh so sánh để kiểm tra. Xét một ví dụ viết bằng C# sau:

using System;

class Program
{
    public static void Main()
    {
        Object obj = new Dog();

        Animal ani = obj as Animal;

        if (ani == null)
            Console.WriteLine("It is not an animal");
        else
            Console.WriteLine("It is an animal");

    }
}
class Animal { }
class Dog:Animal {}

Sau khi dùng lLdasm chuyển sang mã CIL và rút gọn ta được đoạn mã sau:

.assembly extern mscorlib {}
.assembly Conversion{}

.method public static void  main()
{
    .entrypoint
    .maxstack  2
    .locals init (object obj, class Animal ani)

    newobj instance void Dog::.ctor()

    isinst Animal
    ldnull
    bgt isanimal

    ldstr "It is not an animal"
    br print

isanimal:
    ldstr "It is an animal"

print:
    call void [mscorlib]System.Console::WriteLine(string)

    ret
}
.class private Animal
       extends [mscorlib]System.Object
{
  .method public void  .ctor() cil managed
  {

    .maxstack  1
    ldarg.0
    call instance void [mscorlib]System.Object::.ctor()
    ret
  }

}

.class private Dog
       extends Animal
{
  .method public void  .ctor()
  {
    .maxstack 1
    ldarg.0
    call instance void Animal::.ctor()
    ret
  }

}

Như bạn thấy trong phương thức main(), tôi dùng lệnh rẽ nhánh bgt để so sánh một đối tượng instance với null. Đối tượng instance này được kiểm tra bởi lệnh isinst xem đó có phải là kiểu Animal không. Và kết quả xuất ra cho ta biết điều đó:

It is an animal

Dùng phương thức của mscorlib

Một cách thông dùng là dùng phương thức Parse() hoặc Convert() của các lớp như System.Int32, System.String,… Cách này làm đơn giản vì bạn đã quen thuộc với việc gọi phương thức từ thư viện mscorlib. Ta có ví dụ minh họa sau:

.assembly extern mscorlib {}
.assembly Conversion{}

.method public static void  main()
{
  .entrypoint
  .maxstack 1

  ldstr "123"
  call int32 [mscorlib]System.Int32::Parse(string)
  call void [mscorlib]System.Console::WriteLine(int32)

  ret
}

Boxing và Unboxing

Boxing là chuyển một dữ liệu kiểu giá trị thành đối tượng kiểu tham chiếu, unboxing là ngược lại. Khi đó dữ liệu sẽ chuyển sang heap thay vì stack. Để sử dụng boxing ta dùng lệnh box với tham số theo sau tên kiểu tham chiếu tương ứng của dữ liệu trong stack. Ngược lại ta dùng lệnh unbox để unboxing một đối tượng.

Ta thử làm một ví dụ với phương thức Console.WriteLine(string,params object[]) với mã C# sau:

using System;

class Program
{
    public static void Main()
    {
        string s = "{0} + {1} = {2}";

        Console.WriteLine(s, 1, 2, 3);
    }
}

Như bạn thấy các tham số 1, 2, 3 truyền vào là kiểu giá trị trong khi các tham số được khai báo là kiểu object. Như thế trong mã CIL, chúng phải được boxing thành kiểu object.

.assembly extern mscorlib {}
.assembly Boxing{}

.method public static void  main()
{
  .entrypoint
  .maxstack  4

  ldstr "{0} + {1} = {2}"
  ldc.i4.1
  box [mscorlib]System.Int32
  ldc.i4.2
  box [mscorlib]System.Int32
  ldc.i4.3
  box [mscorlib]System.Int32
  call void [mscorlib]System.Console::WriteLine(string,object,object,object)

  ret
}

Output:

1 + 2 = 3

Bài viết liên quan:

MSIL – Part 1: Giới thiệu về MSIL
MSIL – Part 2: Biến cục bộ
MSIL – Part 3: Rẽ nhánh và vòng lặp
MSIL – Part 4: Lập trình hướng đối tượng
MSIL – Part 5: Conversion, casting và boxing
MSIL – Part 6: Sử dụng mảng

https://yinyangit.wordpress.com

Advertisements

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Đăng xuất / Thay đổi )

Connecting to %s