You are on page 1of 36

Đinh Ngọc Điệp CNTT-K36

BÁO CÁO CÔNG NGHỆ .NET


Chương 2

I. ASSEMBLIES
Assemblies là một phần cơ sở lập trình với .NET Framework. Assembly thực hiện
các chức năng sau:

 Nó chứa mã thực thi CLR. Mã ngôn ngữ trung gian (MSIL) chứa trong một tập
tin thực thi di động (PE) sẽ không được thực thi nếu nó không có khai báo
assembly liên kết. Lưu ý assembly chỉ có một điểm bắt đầu (đây là:DllMain ,
WinMain , hoặc hàm Main).
 Nó là dạng giới hạn bảo vệ. Một assembly là đơn vị mà cho phép yêu cầu và
chấp nhận.
 Nó là dạng một kiểu giới hạn. Mỗi loại đặc tính bao gồm tên của assembly mà
nó cư trú. Một loại được gọi là MyType được tải trong phạm vi của một
assembly là không giống như là một loại gọi là MyType được tải trong phạm vi
assembly khác.
 Nó là dạng giới hạn phạm vi tham khảo. Biểu thị của assembly chứa assembly
metadata được sử dụng để giải quyết các loại và đáp ứng yêu cầu tài nguyên.
Nó chỉ rõ các loại và tài nguyên được tiếp xúc bên ngoài assembly. Các biểu thị
cũng liệt kê assemblies khác mà nó phụ thuộc.
 Nó là dạng giới hạn phiên bản. Assembly là đơn vị versionable nhỏ nhất trong
CLR; tất cả các loại và các tài nguyên trong cùng một assembly được dịch như
một đơn vị. Assembly mô tả sự phụ thuộc phiên bản bạn chỉ định cho bất kỳ hội
đồng phụ thuộc. Để biết thêm thông tin về phiên bản, thấy hội Versioning .
 Nó là dạng một đơn vị triển khai. Khi một ứng dụng bắt đầu, chỉ assemblies là
ứng dụng ban đầu gọi phải sẵn sàng. Assemblies khác, chẳng hạn như ví trí các
tài nguyên hoặc assemblie có chứa lớp tiện ích, có thể được khôi phục và yêu
cầu. Điều này cho phép các ứng dụng được giữ đơn giản và nhỏ khi tải xuống
đầu tiên.
 Nó là đơn vị mà sự thực thi side-by-side được hỗ trợ.
Đinh Ngọc Điệp CNTT-K36

Assemblies có thể là tĩnh hoặc động. Assemblies tĩnh có thể bao gồm .NET
Framework các loại (lớp và giao tiếp), cũng như các tài nguyên cho assembly
(bitmap, các file JPEG, file tài nguyên, vv). Assemblies tĩnh được lưu trữ trên đĩa
trong tập tin thực thi di động (PE). Bạn cũng có thể sử dụng .NET Framework để
tạo ra Assemblies động, được chạy trực tiếp từ bộ nhớ và sẽ không được lưu vào
đĩa trước khi thực thi. Bạn có thể lưu Assemblies động vào đĩa sau khi họ đã thực
thi.

 Tập tin PE

Assembly được lưu trữ trên dĩa từ theo dạng thức tập tin Portable Executable
(PE). Dạng thức tập tin PE của .NET cũng giống như tập tin PE bình thường của
Windows NT. Dạng thức PE được cài đặt thành dạng thức tập tin DLL và EXE.

Về mặt logic, assembly chứa đựng một hay nhiều module. Mỗi module được tổ
chức thành một DLL và đồng thời mỗi module là một cấu thành của assembly. Các
module tự bản thân chúng không thể chạy được, các module phải kết hợp với nhau
thành assembly thì mới có thể làm được việc gì đó hữu ích.

 Metadata

Metadata là thông tin được lưu trữ bên trong assembly với mục đích là để mô tả
các kiểu dữ liệu, các phương thức và các thông tin khác về assembly. Do có chứa
metadata nên assembly có khả năng tựmôtả.

 Giới hạn bảo vệ

Assembly tạo ra một giới hạn bảo vệ (security boundary). Các kiểu dữ liệu định
nghĩa bên trong assembly bị giới hạn phạm vi tại ranh giới assembly. Để có thể sử
dụng chung một kiểu dữ liệu giữa 2 assembly, cần phải chỉ định rõ bằng tham
chiếu (reference) trong IDE hoặc dòng lệnh.

 Manifest

Manifest chính là một thành phần của metadata. Manifest mô tả một assembly
chứa những gì, ví dụ như: thông tin nhận dạng (tên, phiên bản), danh sách các kiểu
dữ liệu, danh sách các resource, danh sách các assembly khác được assembly này
tham chiếu đến ...
Đinh Ngọc Điệp CNTT-K36

 Các module trong manifest

Một assembly có thể chứa nhiều module, do đó manifest trong assembly còn có
thể chứa mã băm (hash code) của mỗi module lắp ghép thành assembly để bảo đảm
rằng khi thực thi, chỉ có thể nạp các module đúng phiên bản.

Chỉ cần một sự thay đổi rất rất nhỏ trong module là mã băm sẽ thay đổi.

 Manifest trong module

Mỗi module cũng chứa riêng phần manifest mô tả cho chính nó giống như
assembly chứa manifest mô tả cho assembly vậy.

 Các assembly cần tham chiếu

Manifest của assembly cũng có thể chứa tham chiếu đến các assembly khác.
Mỗi tham chiếu chứa đựng tên, phiên bản, văn hóa (culture), nguồn gốc
(originator),…

Thông tin về nguồn gốc chính là chữ ký số (digital signature) của lập trình viên
hay của công ty nơi cung cấp assembly mà assembly hiện tại đang tham chiếu đến.

Văn hóa là một đối tượng chứa thông tin về ngôn ngữ, cách trình bày của mỗi
quốc gia. Ví dụ như cách thể hiện ngày tháng: D/M/Y hay M-D-Y

 Đa Module Assembly

Một assembly đơn module là một assembly chỉ gồm một module, module này
có thể là một tập tin EXE hoặc DLL. Manifest cho assembly đơn module được
nhúng vào trong module.

Một assembly đa module là một assembly bao gồm nhiều tập tin (ít nhất một
tập tin EXE hoặc DLL). Manifest cho assembly đa module có thể được lưu trữ
thành một tập tin riêng biệt hoặc được nhúng vào một module nào đó bất kỳ.

 Lợi ích của đa module assembly

Nếu một dự án có nhiều lập trình viên mà dự án đó chỉ xây dựng bằng một
assembly, việc kiểm lỗi, biên dịch dự án,… là một “ác mộng” vì tất cả các lập trình
Đinh Ngọc Điệp CNTT-K36

viên phải hợp tác với nhau, phải kiểm tra phiên bản, phải đồng bộ hóa mã
nguồn,…

Nếu một ứng dụng lớn được xây dựng bằng nhiều assembly, khi cần cập nhật
(update) để sửa lỗi chẳng hạn, thì chỉ cần cập nhật một / vài assembly mà thôi.

Nếu một ứng dụng lớn được tổ chức từ nhiều assembly, chỉ có những phần mã
chương trình thường sử dụng / quan trọng thuộc một vài assembly là được nạp vào
bộ nhớ, do đó làm giảm bớt chi phí bộ nhớ, tăng hiệu suất hệ thống.

 Assembly nội bộ (private assembly)

Có 2 loại Assembly: nội bộ (private) và chia sẻ (shared). Assembly nội bộ được


dự định là chỉ dùng cho một ứng dụng, còn assembly chia sẻ thì ngược lại, dùng
cho nhiều ứng dụng.

Các assembly nội bộ được ghi trên dĩa từ thành một tập tin EXE hoặc DLL
trong cùng thư mục với assembly chính hoặc trong các thư mục con của thư mục
chứa assembly chính. Để thực thi trên máy khác chỉ cần sao chép đúng cấu trúc thư
mục là đủ, không cần phải đăng ký với Registry.

Ví dụ:

Tạo một project thêm class library như sau:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ClassA
{
public class Class1
{
public void hienThi(String name)
{
Console.WriteLine("Xin chao ban " + name);
}
}
}
Sau đó build ra file ClassA.DLL.
Đinh Ngọc Điệp CNTT-K36

Minh họa sử dụng Assembly:

Tạo project với class sau ,sau đó add refrences là ClassA.dll (file ClassA.dll
sinh ra từ project trước nằm trong thư mục bin\debug).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace sudungAssembly
{
class Program
{
static void Main(string[] args)
{
ClassA.Class1 a = new ClassA.Class1();
a.hienThi("Dinh Ngoc Diep");
Console.ReadLine();
}
}
}
Kết quả minh họa:

 Assembly chia sẻ (shared assembly)


Đinh Ngọc Điệp CNTT-K36

Khi viết ra một assembly đại loại như một control chẳng hạn, nếu tác giả của
control đó có ý định chia sẻ cho các lập trình viên khác thì anh / chị ta phải xây
dựng assembly đó đáp ứng các yêu cầu sau:

- Assembly đó phải có tên “mạnh” (strongname). Tên mạnh có nghĩa là chuỗi


biểu diễn tên đó phải là duy nhất (globallyunique)
- Phải có thông tin về phiên bản để tránh hiện tượng các phiên bản “dẫm chân
lên nhau”

Để có thể chia sẻ assembly, assembly đó phải được đặt vào nơi gọi là Global
AssemblyCache(GAC). Đây là nơi được quy định bởi Common Language
Runtime (CLR) dùng để chứa assembly chia sẻ.

Ví dụ minh họa:

Mở Visual studio command prompt (với quyền admin)

Gõ lệnh như sau:

Tạo 1 project tên ClassA ,thêm class library(class1.cs)


Copy file key.snk vào chung folder với class library (class1.cs)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
[assembly: AssemblyKeyFile("key.snk")]
Đinh Ngọc Điệp CNTT-K36

//chú ý :nếu không copy key.snk vào chung thư mục thì phải chỉ
//rõ đường dẫn
namespace ClassA
{
public class Class1
{
public void hienThi(String name)
{
Console.WriteLine("Xin chao ban " + name);
}
}
}
Giờ ta sẽ copy file ClassA.dll (nằm trong thư mục bin\debug của project
ClassA) đến thư mục C:\ (mục đích để dễ gõ lệnh)

Mở Visual studio command prompt (với quyền admin)

Tạo một project mới để sử dụng ClassA.dll như sau:

(sau đó add refrences là ClassA.dll ( thư mục C:\ClassA.dll) )


using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using ClassA;
namespace sudungAssembly
{
Đinh Ngọc Điệp CNTT-K36

class Program
{
static void Main(string[] args)
{
Class1 a = new Class1();
a.hienThi("Dinh Ngoc Diep");
Console.ReadLine();
}
}
}

Kết quả :

 Xung đột DLL

Giả sử bạn cài đặt một ứng dụng A lên một máy và nó chạy tốt. Sau đó bạn cài
đặt ứng dụng B, bỗng nhiên ứng dụng A không chịu hoạt động. Sau quá trình tìm
hiểu, cuối cùng nguyên nhân là do ứng dụng B đã cài một phiên bản khác đè lên
một tập tin DLL mà ứng dụng A sử dụng. Tình huống trên gọi là “xung đột DLL”

Sự ra đời của assembly đã chấm dứt tình trạng trên.

 Tên mạnh
Đinh Ngọc Điệp CNTT-K36

Một tên mạnh là một chuỗi các ký tự hexa mang thuộc tính là duy nhất trong
toàn cầu (globally unique). Ngoài ra chuỗi đó còn được mã hóa bằng thuật toán
khóa công khai

Mã hóa khóa công khai – bí mật: đó là một thuật toán mã hóa đặc biệt, đầu tiên
dùng một thuật toán riêng tạo ra 2 khóa, một khóa phổ biến rộng rãi nên gọi là
khóa công khai, khóa còn lại do chủ nhân của nó cất nơi an toàn nên gọi là bí mật.
Sau đó dùng thuật toán mã hóa để mã hóa dữ liệu. Một khi dữ liệu bị mã hóa bằng
một khóa thì dữ liệu đó chỉ có thể được giải mã bằng khóa kia và ngược lại.Để bảo
đảm rằng assembly không bị thay đổi vô tình hay cố ý.

Để tạo ra tên mạnh, một cặp khóa công khai-bí mật được tạo ra cho assembly.
Một mã băm (hash code) được tạo ra từ tên, nội dung của các tập tin bên trong
assembly và chuỗi biểu diễn khóa công khai. Sau đó mã băm này được mã hóa
bằng khóa bí mật, kết quả mã hóa được ghi vào manifest. Quá trình trên được gọi
là ký xác nhận vào assembly (signing the assembly).

Khi assembly được CLR nạp vào bộ nhớ, CLR sẽ dùng khóa công khai trong
manifest giải mã mã băm để xác định xem assembly có bị thay đổi không.

 Global Assembly Cache (GAC)

Sau khi đã tạo tên mạnh và ghi vào assembly, việc còn lại để thực hiện chia sẻ
assembly là đặt assembly đó vào thư mục GAC. Đó là một thư mục đặc biệt dùng
để chứa assembly chia sẻ. Trên Windows, đó là thư mục \WinNT\assembly hoặc
C:\Windows\Assembly.

 Ví dụ minh họa:

II. SỐ HIỆU PHIÊN BẢN (VERSIONING)

Mỗi assembly có số hiệu phiên bản riêng. Một “phiên bản” ám chỉ toàn bộ nội
dung của một assembly bao gồm cả kiểu dữ liệu và resource.

Assembly chia sẻ trong .NET được định vị bằng tên duy nhất (unique) và phiên
bản. Phiên bản được biểu diễn bởi 4 số phân cách bằng dấu ‘:’ ví dụ như
1:2:6:1246
Đinh Ngọc Điệp CNTT-K36

Số đầu tiên mô tả phiên bản chính (major version)

Số thứ 2 mô tả phiên bản phụ (minor version)

Số thứ 3 mô tả thứ tự bản xây dựng (build)

Số cuối cùng mô tả lần xem xét cập nhật (revision) để sửa lỗi

III. REFLECTION
Reflection được hiểu là một chức năng trong .Net cho phép đọc thông tin từ
các siêu dữ liệu (metadata) của assembly để tạo ra một đối tượng (có kiểu là
Type) bao gói các thông tin đó lại. Với reflection, bạn có thể trích xuất để
gọi và tạo ra các phương thức, truy cập và thay đổi các thuộc tính của đối
tượng một cách linh động trong quá trình runtime.

1. Phương thức GetType() và toán tử typeof:

Ta có thể dùng phương thức GetType() của lớp Object để trả về đối
tượng kiểu Type mô tả kiểu dữ liệu của đối tượng. Có được đối tượng Type
này rồi, ta sẽ lấy thông tin của kiểu dữ liệu qua các thuộc tính của lớp Type.

Sau đây là một ví dụ đơn giản minh họa cách in ra tên kiểu dữ liệu của các
biến qua thuộc tính FullName của lớp Type:

static void Main(string[] args)


{
var number = 100;
var text = "abc";
Console.WriteLine(number.GetType().FullName);
Console.WriteLine(text.GetType().FullName);
Console.Read();
}

Phương thức GetType() chỉ có thể lấy được thông tin từ các biến đối
tượng. Trong trường hợp muốn lấy thông tin của lớp thông qua tên lớp, bạn
phải sử dụng phương thức tĩnh Type.GetType(), tuy nhiên tham số truyền
vào cần phải ghi đầy đủ cả namespace.
Đinh Ngọc Điệp CNTT-K36

static void Main()


{
// Không được ghi tham số là string, int hoặc Int32
Type mType1 = Type.GetType("System.Int32");
Type mType2 = Type.GetType("System.String");

Console.WriteLine(mType1.FullName);
Console.WriteLine(mType2.FullName);

Console.Read();
}
Sử dụng toán tử typeof, bạn có thể lấy về đối tượng kiểu System.Type của
bất kì kiểu nào với cú pháp typeof(type).
static void Main()
{
Console.WriteLine(typeof(Int32).FullName);
Console.WriteLine(typeof(String).FullName);
Console.Read();
}
Cả ba ví dụ trên đều cho ra cùng kết quả khi được thực thi:

2. Lấy các thông tin từ Type

Lớp Type cung cấp đầy đủ các phương thức cho phép lấy các thông tin của
kiểu dữ liệu. Các phương thức này có dạng GetXXX(), mỗi phương thức trả
Đinh Ngọc Điệp CNTT-K36

về một hay một mảng đối tượng lưu trữ thông tin của mỗi thành viên trong
kiểu dữ liệu (bạn có thể nhận biết điều này thông qua cách đặt tên của các
phương thức ở dạng số nhiều hay số ít). Các kiểu trả về của các phương thức
này đều có hậu tố là Info: ConstructorInfo, EventInfo, FieldInfo,
InterfaceInfo, MemberInfo, MethodInfo, PropertyInfo.

Ví dụ để lấy tất cả thành viên public của một lớp ta dùng phương thức
GetMembers() như minh họa dưới đây:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace MinhHoa
{

class Program
{
class Nguoi
{ // Chỉ có mục đích minh họa
public string ten;
public static int tuoi;
public void hienThi() { }
}
static void Main()
{
Type mType = typeof(Nguoi);
MemberInfo[] m = mType.GetMembers();
Array.ForEach(m, mem =>
Console.WriteLine(mem.MemberType.ToString().PadRight(12) +
":" + mem));

Console.Read();
}
}
}
Khi chạy đoạn mã này bạn sẽ nhận được kết quả như sau:
Đinh Ngọc Điệp CNTT-K36

3. Ví dụ về MethodInfo: thực thi một phương thức

Ta sẽ trở lại với phần mở đầu của bài viết mà tôi nói về trường hợp thực thi
một phương thức dựa vào tên mà người dùng truyền vào. Hãy xem các
phương thức mà lớp Type cung cấp, có một phương thức tên là
GetMethod(string methodName). Giá trị trả về của phương thức này là một
đối tượng kiểu System.Reflection.MethodInfo chứa các thông tin về phương
thức. Và đây là một ví dụ đơn giản minh họa cách dùng phương thức này:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace MinhHoa
{

class Program
{
class MyClass
{
public void SayHello()
{
Console.WriteLine("Xin chao ban");
}
}
Đinh Ngọc Điệp CNTT-K36

static void Main()


{
MyClass myClass = new MyClass();
Type m = myClass.GetType();
// Lấy về phương thức SayHello
MethodInfo myMethodInfo =
m.GetMethod("SayHello");
// Thực thi phương thức SayHello của myClass
với tham số là null

myMethodInfo.Invoke(myClass, null);
Console.Read();
}

}
}

Kết quả xuất ra là:

Bởi vì phương thức SayHello() trên không yêu cầu tham số nên ta sẽ
truyền null vào phương thức Invoke() của đối tượng MethodInfo. Đối với
các phương thức yêu cầu tham số, ta phải tạo một mảng object[] để truyền
giá trị vào. Hãy xem ví dụ sau:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
Đinh Ngọc Điệp CNTT-K36

namespace MinhHoa
{

class Program
{
class MyClass
{
public void SayHello(String name)
{
Console.WriteLine("Xin chao ban "+name);
}
}

static void Main()


{
MyClass myClass = new MyClass();
Type m = myClass.GetType();
// Lấy về phương thức SayHello
MethodInfo myMethodInfo =
m.GetMethod("SayHello");
// Thực thi phương thức SayHello của myClass
với tham số là null
object[] ten = new object[] { "Dinh Ngoc Diep"
};
myMethodInfo.Invoke(myClass, ten);
Console.Read();
}

}
}

Kết quả xuất ra:


Đinh Ngọc Điệp CNTT-K36

Trong trường hợp có nhiều overload của phương thức SayHello(), nếu
bạn sử dụng phương thức GetMethod() trên thì sẽ nhận một exception với
thông báo “Ambiguous match found” trong lúc runtime. Nguyên nhân là do
chương trình không thể biết được phải lấy phương thức SayHello() nào. Lúc
đó bạn phải dùng một overload khác của GetMethod() để xác định các tham
số sẽ truyền vào phương thức SayHello(), và đây là ví dụ:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace MinhHoa
{
class Program
{
class MyClass
{
public void SayHello()
{
Console.WriteLine("Xin chao ban");
}
public void SayHello(string name)
{
Console.WriteLine("Xin chao ban "+ name);
}
}
static void Main()
{
Đinh Ngọc Điệp CNTT-K36

MyClass myClass = new MyClass();


Type m = myClass.GetType();

// Lấy về phương thức SayHello có 1 tham số


kiểu string
MethodInfo myMethodInfo =
m.GetMethod("SayHello", new Type[] { typeof(string) });
// Thực thi phương thức SayHello của myClass
với tham số
myMethodInfo.Invoke(myClass, new object[] {
"Dinh Ngoc Diep"});
Console.Read();
}
}

Kết quả xuất ra:

Thay vì dùng phương thức GetMethod(), bạn có thể dùng trực tiếp
phương thức instance của lớp Type là InvokeMember() với tham số thứ hai
là BindingFlags. InvokeMethod.:
public object InvokeMember(
string name,
BindingFlags invokeAttr,
Binder binder,
object target,
object[] args
);
Đinh Ngọc Điệp CNTT-K36

Ví dụ trên có thể viết lại như sau:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace MinhHoa
{
class Program
{
class MyClass
{
public void SayHello()
{
Console.WriteLine("Xin chao ban");
}
public void SayHello(string name)
{
Console.WriteLine("Xin chao ban "+ name);
}
}
static void Main()
{
MyClass myClass = new MyClass();
Type m = myClass.GetType();
m.InvokeMember("SayHello",
BindingFlags.InvokeMethod, null, myClass, null);
Console.Read();
}
}
}
4. Ví dụ về ConstructorInfo: Tạo instance của đối tượng
Đây là vấn đề khá thực tế trong .Net, trước đây cũng có nhiều bạn
thắc mắc về cách viết một phương thức tạo ra một instance của Form với
tham số truyền vào là tên của Form đó ở dạng chuỗi. Vấn đề này không khó
nếu như bạn đã biết cách dùng Reflection.

o Sử dụng
Khám phá các thành viên của lớp Type, bạn có thể đoán ra là có thể sử
dụng lớp ConstructorInfo để làm điều này.
Đinh Ngọc Điệp CNTT-K36

Phương thức GetConstructor(Type[] types) của lớp Type yêu cầu một
mảng kiểu Type theo đúng kiểu, trình tự và số lượng tham số mà constructor
cần lấy về yêu cầu. Trong trường hợp không có tham số, lớp Type cung cấp
sẵn một mảng Type rỗng là Type.EmptyTypes. Hãy xem hai ví dụ sau:

– Constructor không có tham số (dùng default constructor):


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace MinhHoa
{
class Program
{
class MyClass
{
public void SayHello()
{
Console.WriteLine("Xin chao ban");
}
public void SayHello(string name)
{
Console.WriteLine("Xin chao ban "+ name);
}
}
static void Main()
{
Type mType =
Type.GetType("MinhHoa.Program+MyClass");
ConstructorInfo conInfo =
mType.GetConstructor(Type.EmptyTypes);
object obj = conInfo.Invoke(null);
mType.InvokeMember("SayHello",
BindingFlags.InvokeMethod, null, obj, null);
Console.ReadLine();
}

}
}

– Constructor có tham số:


Đinh Ngọc Điệp CNTT-K36

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace MinhHoa
{
class Program
{
class MyClass
{
string name;
public MyClass(string name)
{
this.name = name;
}

public void SayHello()


{
Console.WriteLine("Xin chao ban "+name);
}
public void SayHello(string name)
{
Console.WriteLine("Xin chao ban "+ name);
}
}
static void Main()
{
Type mType =
Type.GetType("MinhHoa.Program+MyClass");
ConstructorInfo conInfo =
mType.GetConstructor(new Type[] { typeof(string) });
object obj = conInfo.Invoke(new object[] {
"Dinh Ngoc Diep" });
mType.InvokeMember("SayHello",
BindingFlags.InvokeMethod, null, obj, null);
Console.ReadLine();
}
}
}
Kết quả hiển thị:
Đinh Ngọc Điệp CNTT-K36

Một chút thay đổi và bạn có thể dễ dàng hiểu được. Tuy nhiên có thể
bạn thắc mắc về chuỗi tham số mà tôi sử dụng trong phương thức
Type.GetType(). Nếu để ý bạn có thể thấy MyClass là một lớp nằm bên
trong lớp Program (inner class), và để truy xuất đến lớp con ta phải dùng dấu
‘+’ để ko bị nhầm lẫn coi lớp Program là tên một namespace.

5. Lớp System.Activator

Bạn cảm thấy cách tạo instance như trên khá rắc rối, khó nhớ, và thực tế là
có một cách khác để làm điều này, mặc dù về căn bản nó vẫn là một. Như đã
nói trong phần trước, bởi vì yêu cầu tạo một instance từ tên kiểu khá phổ
biến, .Net cung cấp sẵn cho ta lớp System.Activator để làm được điều này.
Một giải pháp “nhỏ gọn” và giúp lập trình viên không phải dính tới những lí
thuyết rườm rà mà ít khi được ứng dụng trong công việc.

Lớp System.Activator này sử dụng một phần của System.Type để tạo ra


những phương thức tĩnh cho phép người dùng sử dụng bất cứ lúc nào, đơn
giản và tiện lợi.

Hãy xem những gì mà System.Activator thể hiện và so sánh với


System.Type, bạn có thể nhận thấy sự tương đồng của 2 lớp riêng biệt này.
Tôi sử dụng hai lớp MyClass1 và MyClass2 để minh họa cho việc tạo đối
tượng với constructor không và có tham số.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace MinhHoa
{
class Program
{
Đinh Ngọc Điệp CNTT-K36

class MyClass1
{
public void SayHello()
{
Console.WriteLine("Xin chao ban ");
}
}
class MyClass2
{
string name;
public MyClass2(string name){this.name = name;}
public void SayHello()
{
Console.WriteLine("Xin chao ban " + name);
}
}
static void Main(string[] args)
{
Type mType1 =
Type.GetType("MinhHoa.Program+MyClass1");
Type mType2 =
Type.GetType("MinhHoa.Program+MyClass2");
object obj1 = Activator.CreateInstance(mType1);
object obj2 = Activator.CreateInstance(mType2,
"Dinh Ngoc Diep");
mType1.InvokeMember("SayHello",
BindingFlags.InvokeMethod, null, obj1, null);
mType2.InvokeMember("SayHello",
BindingFlags.InvokeMethod, null, obj2, null);
Console.Read();
}
}

}
Kết quả minh họa:
Đinh Ngọc Điệp CNTT-K36

6. Lớp System.Reflection.Assembly

Trong bài viết này tôi cũng có vài lần nhắc đến từ assembly, đây là
một khái niệm cơ bản trong .Net. Cũng xin nhắc lại một chút về khái niệm
này nếu như bạn đã bỏ lỡ nó khi bắt đầu tìm hiểu về .Net.

– Định nghĩa:.Net Assembly có thể được hiểu là kết quả của quá trình biên
dịch từ mã nguồn sang tập tin nhị phân dựa trên .Net framework. Là thành
phần cơ bản nhất .Net framework, assemly là thành phần không thể thiếu
trong bất kì ứng dụng .Net nào và được thể hiện dưới hai dạng tập tin là
EXE (process assembly) và DLL (library assembly). Assembly có thể được
lưu trữ dưới dạng single-file hoặc multi-file, tùy theo kiểu dự án mà bạn làm
việc.

Lớp System.Reflection.Assembly có thể được coi là một kiểu mô


phỏng chi tiết về các assembly. Lớp Assembly chứa đầy đủ các thông tin
cho phép chúng ta truy xuất thông tin và thực thi các phương thức lấy được
từ các assembly. Có thể hiểu một cách tương tự: Lớp Type đại diện cho các
kiểu dữ liệu, lớp Assembly đại diện cho các assembly.
Đinh Ngọc Điệp CNTT-K36

Lớp Assembly cung cấp các phương thức tĩnh để nạp một assembly
thông qua AssemblyName, đường dẫn tập tin hoặc từ tiến trình đang chạy.
Bạn cũng có thể lấy Assembly dễ dàng từ property của lớp Type, ví dụ
typeof(int).Assembly.

Từ Assemly lấy được, bạn có thể dùng phương thức GetTypes() lấy
về các kiểu dữ liệu và thực hiện các kĩ thuật giống như trong phần về lớp
System.Type tôi đã nói tới.

Sau đây là một ví dụ đơn giản sử dụng phương thức


Assembly.GetExecutingAssembly() để lấy về assembly hiện tại và in ra màn
hình các kiểu dữ liệu của nó:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace MinhHoa
{
class Program
{
class MyClass1
{
public void SayHello()
{
Console.WriteLine("Xin chao ban ");
}
}
class MyClass2
{
string name;
public MyClass2(string name){this.name = name;}
public void SayHello()
{
Console.WriteLine("Xin chao ban " + name);
}
}
static void Main(string[] args)
{
Assembly ass = Assembly.GetExecutingAssembly();
Type[] mTypes = ass.GetTypes();
Đinh Ngọc Điệp CNTT-K36

Array.ForEach(mTypes, type =>


Console.WriteLine(type.Name));
Console.Read();

}
}

}
Kết quả xuất ra:

IV. COLLECTION (TẬP HỢP)


Đơn giản nhất Collection là một tập hợp các dữ liệu không cùng kiểu.
Collection cung cấp rất nhiều các phương thức giúp người dùng thao tác với dữ
liệu một cách đơn giản và dễ dàng.
Một trong những ưu điểm lớn nhất của Collection là khả năng tương tác và thay
đổi dữ liệu bên trong nó ngay tại thời điểm chạy (run-time).
Microsoft cung cấp một tập hợp các lớp Collections trong thư viện
System.Collections. Bên dưới đây là một bảng liệt kê những lớp Collection được
sử dụng phổ biến.
Đinh Ngọc Điệp CNTT-K36

Chi tiết về các lớp Collection trong namespace System.Collections


Namespace System.Collection
Đây là namspace chứa các lớp để thao tác với tập các đối tượng (tập hợp). Dưới
đây là bảng liệt kê các lớp và interface có trong namespace này
1. ArrayList
ArrayList là một collection cho phép lưu trữ dữ liệu theo kiểu mảng (truy cập các
giá trị bên trong đối tượng này thông qua chỉ số index – chỉ mục). Về cơ bản
ArrayList khá giống mảng Object. Tuy nhiên khác với mảng, bạn có thể dễ dàng
thêm hay bớt các phần tử bên trong ArrayList ngay tại thời điểm chạy, kích cỡ của
ArrayList sẽ tự động thay đổi theo.
Dưới đây là các phương thức và thuộc tính thông dụng của lớp ArrayList
Đinh Ngọc Điệp CNTT-K36

Đáng chú ý nhất trong các thuộc tính này là thuộc tính Count. Đây là thuộc tính trả
về số lượng các phần tử có trong ArrayList.

Trong
các
phương thức này, đáng chú ý nhất có thể kể đến:
+ Add – thêm mới một đối tượng vào ArrayList
+ Remove – loại bỏ đối tượng khỏi ArrayList
+ Insert – Chèn một đối tượng mới vào ArrayList tại vị trí index cụ thể.
+ Sort – Sắp xếp ArrayList
Ví dụ với ArrayList:
Đinh Ngọc Điệp CNTT-K36

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace MinhHoa
{
class Program
{
static void Main(string[] args)
{
//Khởi tạo đối tượng
ArrayList al = new ArrayList();
//Thêm dữ liệu
al.Add(1);
al.Add(2);
al.Add(3);
//Sửa giá trị tại vị trí thứ 2 thành 10. Vị trí
thứ 2 có index = 1, do index bắt đầu từ 0
al[1] = 10;
//Xóa phần tử có giá trị bằng 3
al.Remove(3);
//Duyệt bằng chỉ mục index
for (int index = 0; index < al.Count; index++)
{
Console.WriteLine(al[index].ToString());
}
Console.WriteLine();
//Duyệt với foreach
foreach (int i in al)
{
Console.WriteLine(i);
}
Console.ReadLine();
}
}
}

Kết quả minh họa:


Đinh Ngọc Điệp CNTT-K36

2. Hashtable
Lớp Hashtable cho phép lưu trữ giá trị theo các cặp Key-Value. Thông qua các
khóa (Key), người dùng có thể dễ dàng thao tác đến các giá trị (Value) bên trong
Hashtable.
Chú ý: Do Hashtable sử dụng khóa để thao tác, nên các giá trị key này phải là duy
nhất.
Dưới đây là các phương thức và thuộc tính thông dụng của lớp Hashtable:
- Trong đó:
+ Count: Trả về số lượng các cặp key-value có trong Hashtable.
+ Keys: Trả về danh sách các Key trong Hashtable
+ Values: Trả về danh sách các Value trong Hashtable
- Trong đó:
+ Add: Phương thức cho phép thêm mới một cặp key và value vào
Hashtable. Để đảm bảo khóa Key không bị trùng, nên kiểm tra trước với
phương thức ContainsKey.
+ Remove: Xóa cặp key và value có trong Hashtable thông qua tham số key.
+ ContainsKey: Kiểm tra xem khóa key đã có trong Hashtable hay chưa.
Ví dụ với Hashtable:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace MinhHoa
{
class Program
Đinh Ngọc Điệp CNTT-K36

{
static void Main(string[] args)
{
//Khởi tạo đối tượng
Hashtable ha = new Hashtable();
//Thêm mới
ha.Add("hello", "Xin chao");
ha.Add("i", "toi");
ha.Add("they", "ho");
//Kiểm tra tồn tại
if (ha.ContainsKey("i"))
{
Console.WriteLine("Key=\"i\" đã có");
Console.WriteLine();
}
//Thay đổi giá trị của cặp có Key="they" thành
"no"
if (ha.ContainsKey(2))
{
ha["they"] = "no";
}
//Xóa key="they"
ha.Remove("they");
//Duyệt qua toàn bộ Key
foreach (String key in ha.Keys)
{
Console.WriteLine("Key: " + key);
Console.WriteLine("Value: " + ha[key]);
Console.WriteLine();
}
Console.WriteLine();
//Duyệt qua toàn bộ Value
foreach (String value in ha.Values)
{
Console.WriteLine("Value: " + value);
Console.WriteLine();
}
Console.ReadLine();
}
}
}
Đinh Ngọc Điệp CNTT-K36

Kết quả:

2.Lớp LinkedList

Phương thức
AddAfter(node, value) Thêm vào sau node giá trị value
AddAfter(node1, node2) Thêm node2 vào sau node1
AddFirst(value) Thêm value vào đầu danh sách
AddLast(value) Thêm value vào cuối danh sách
Clear() Xóa danh sách
RemoveFirst() Xóa phần tử đầu danh sách
RemoveLast() Xóa phần tử cuối danh sách
Remove(value) Xóa phần tử có giá trị là value
GetEnumerator() Lấy Enumerator của danh sách
Find(value) Trả về node có giá trị value đầu tiên
FindLast(value) Trả về node có giá trị value cuối
CopyTo(array[],index) Copy value của danh sách vào mảng array ,index là vị trí
của mảng array bắt đầu chép vào
Thuộc tính
Count Số phần tử của danh sách
First Phần tử đầu danh sách
Last Phần tử cuối danh sách
Đinh Ngọc Điệp CNTT-K36

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace MinhHoa
{
class Program
{
static void Main(string[] args)
{
LinkedList<String> list = new LinkedList<String>();
list.AddLast( "xin");
list.AddLast("chao");
list.AddLast("Ban");
list.AddLast("Dinh Ngoc Diep");
IEnumerator e = list.GetEnumerator();
while (e.MoveNext())
{
Console.WriteLine(e.Current.ToString());
}
Console.WriteLine("---------------------------------
--------");
String[] hoctap = { "Cong nghe .NET", "Thuc hanh lam
viec nhom", "Co so du lieu" };
foreach (String s in hoctap)
list.AddLast(s);
e = list.GetEnumerator();
while (e.MoveNext())
{
Console.WriteLine(e.Current.ToString());
}
Console.WriteLine("---------------------------------
--------");
list.RemoveFirst();
e = list.GetEnumerator();
while (e.MoveNext())
{
Console.WriteLine(e.Current.ToString());
}
Console.ReadLine();
}
}
}

Kết quả:
Đinh Ngọc Điệp CNTT-K36

2.Lớp SortList:

SortedList là lớp kết hợp của Hashtable và ArrayList, có nghĩa là các phần tử
trong tập hợp bao gồm key và value, các phần tử được truy xuất theo chỉ số. Sự
khác nhau giữa Hastable và SortedList là các phần tử trong SortedList được sắp
xếp theo key của nó. Việc thực thi chương trình sử dụng SortedList chậm hơn
chương trình sử dụng Hashtable do có quá trình sắp xếp các phần tử. Bảng dưới
đây liệt kê các phương thức và thuộc tính thông dụng của lớp SortedList.
Phương thức
Add Thêm phần tử
Remove Xóa phần tử
GetKey Trả về key của phần tử có chỉ số i
IndexOfKey Trả về chỉ số của một key
IndexOfValue Trả về chỉ số của value đầu tiên trong danh sách
ContainsKey Tìm kiếm key trong danh sách
getByIndex Trả về value ở vị trí index
Thuộc tính
Capacity Trả về số phần tử mà SortedList có thể chứa
Count Số phần tử trong SortedList
Đinh Ngọc Điệp CNTT-K36

Keys Trả về tập key có trong SortedList


Values Trả về tập values có trong SortedList

Ví dụ minh họa:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace MinhHoa
{
class Program
{
static void Main(string[] args)
{
SortedList list = new SortedList();
list.Capacity = 10;//thiết lập list chứa được 100
phần tử
for(int i=1;i<10;i++)
list.Add(i, "so "+i);
Console.WriteLine("Key=>value");
for (int i = 0; i < list.Count; i++)
Console.WriteLine(list.GetKey(i).ToString() +
"=>" + list.GetByIndex(i).ToString());
Console.ReadLine();
}
}
}
Kết quả minh họa:
Đinh Ngọc Điệp CNTT-K36

3.Lớp Dictionary:

Là lớp tạo ra tập hợp các phần tử kiểu generic gồm có key và value, có nghĩa là
nó chỉ cho phép lưu một tập các phần tử có cặp key và value có cùng kiểu dữ liệu.
Dictonary không có cho phép lưu trữ giá trị null.
Cú pháp: Dictionary
Bảng dưới đây liệt kê các phương thức và thuộc tính thông dụng của lớp
Dictionary.

Phướng thức
Add Thêm phần tử
Remove Xóa phần tử
Clear Xóa tất cả key/value trong Dictionary
ContainsKey Tìm kiếm key trong Dictionary
ContainsValue Tìm kiếm value trong Dictionary
Thuộc tính
Count Số phần tử trong Dictionary
Keys Trả về tập key có trong Dictionary
Values Trả về tập value có trong Dictionary

Ví dụ minh họa:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace MinhHoa
{
class Program
{
static void Main(string[] args)
{
Dictionary<int,String> dic=new
Dictionary<int,string>();
for (int i = 1; i < 10; i++)
dic.Add(i, "So thu tu:" + i);
ICollection c = dic.Keys;
foreach (int k in c)
Console.WriteLine(k + "=>" + dic[k]);
Console.ReadLine();
Đinh Ngọc Điệp CNTT-K36

}
}
}

Kết quả minh họa:

You might also like