1. Những điểm mới trong C# 3.0
1. Tự động hiện thực các thuộc tính
(Auto implemented properties)
Một việc làm khá nhàm chán mà các lập trình viên hay phải làm sau khi tạo lớp là viết
những accessor (còn gọi là setter, getter trong Java) hay có thể hiểu là các property của đối
tượng. Trong C# cũng cung cấp sẵn chức năng tự động tạo ra các accessor từ các field (có
thể hiểu là biến toàn cục trong lớp) có sẵn thông qua menu Refactor> Encapsulate Field…
Tuy nhiên có lẽ điều này vẫn chưa đủ, vì thế trong phiên bản 3.0 đã có một cải tiến mới
giúp giảm bớt thời gian, thao tác bằng cách rút ngắn mã lệnh cần phải viết cho công đoạn
này.
Ví dụ ta tạo một lớp SinhVien với một số thuộc tính tượng trưng theo phương pháp thông
thường của C# như sau:
C# 2.0:
Code:
01 class Student
02 {
03 private int id;
04
05 public int ID
06 {
07 get { return id; }
08 set { id = value; }
09 }
10
11 private string name;
12
13 public string Name
14 {
15 get { return name; }
16 set { name = value; }
17 }
18
19 private DateTime birthDay;
20
21 public DateTime BirthDay
22 {
23 get { return birthDay; }
24 set { birthDay = value; }
25 }
26 }
Có thể thấy là chỉ với 3 field ta đã tốn khá nhiều dòng để tạo ra các property tương ứng cho
lớp SinhVien này. Bây giờ ta thử tạo lớp SinhVien với cấu trúc tương tự trong C# 3.0:
2. C# 3.0:
Code:
1 class Student
2 {
3 public int ID { get; set; }
4 public string Name { get; set; }
5 public DateTime BirthDay { get; set; }
6 }
Một sự khác biệt đơn giản giữa hai đoạn mã của cùng một ngôn ngữ nhưng với hai phiên
bản khác nhau giúp cho công việc viết mã trở nên dễ dàng hơn. Bạn có thể tiết kiệm được
khá nhiều mã lệnh cần phải viết cho một lớp đồng thời giúp cho chúng dễ đọc hơn.
Trình biên dịch sẽ tự động tạo ra các biến private vô danh để lưu giữ giá trị của mỗi
property này.
Trong trường hợp muốn thuộc tính chỉ cho phép đọc (read-only) bạn có thể thêm từ khóa
private vào trước set ví dụ như với thuộc tính ID sau:
view source
Code:
1 class Student
2 {
3 public int ID { get; private set; }
4 public string Name { get; set; }
5 public DateTime BirthDay { get; set; }
6 }
2. Khởi tạo đối tượng
(Object initializers)
Thông thường khi tạo một lớp ta phải overload một số hàm khởi tạo để sử dụng theo từng
trường hợp khác nhau. Mỗi overload xác định những tham số nào cần gán cho các thuộc
tính của đối tượng, công đoạn cũng khá nhàm chán và có thể phải cần đến số lượng dòng
lệnh khá lớn. Để giải quyết vấn đề này, C# 3.0 cung cấp cho ta một cách viết mới cho phép
gán giá trị trực tiếp cho các field hoặc property cần thiết của đối tượng được tạo chỉ trong
một dòng lệnh.
Ví dụ với lớp SinhVien ở trên, bạn không tạo ra một hàm khởi tạo cụ thể nào cả và sử dụng
hàm khởi tạo mặc định để tạo ra một đối tượng SinhVien với các thông tin ban đầu.
C# 2.0:
view source
Code:
1 SinhVien sv = new SinhVien();
2 sv.MaSo = 1;
3 sv.HoTen = "Yin Yang";
4 sv.NgaySinh = new DateTime(2000, 1, 1);
3. Trong C# 3.0 bạn cần một dòng duy nhất để làm điều này:
C# 3.0:
Code:
1 Student sv = new Student{ ID = 1, Name = "Yin Yang", BirthDay = new
DateTime(2000, 1, 1) };
Để mã nguồn dễ đọc, bạn có thể định dạng lại như sau:
view source
Code:
1 Student sv = new Student
2 {
3 ID = 1,
4 Name = "Yin Yang",
5 BirthDay = new DateTime(2000, 1, 1)
6 };
3. Khởi tạo tập hợp
(Collection initializers)
Đối với khởi tạo một tập hợp và thêm vào một số phần tử, bạn có thể khởi tạo nó như một
mảng. Giả sử ta cần một tập hợp có định kiểu để lưu danh sách các SinhVien.
Code:
List<Student> list = new List<Student>();
Theo cách thông thường ta phải dùng phương thức Add() để thêm từng đối tượng kiểu
SinhVien vào danh sách. Trong C# 3.0 ta có thể làm điều này ngay trong lúc khởi tạo tập
hợp:
view source
Code:
1 List<Student> list = new List<Student>{
2 new Student{ID=1,Name="Yin"},
3 new Student{ID=2,Name="Yang"},
4 new Student{ID=3,Name="Zen"}
5 };
4. Biến cục bộ có kiểu ngầm định
(Implicitly typed local variables)
Trong C# 3.0 chúng ta có thể khai báo các biến cục bộ với kiểu không xác định thông qua
từ khóa var. Khi được biên dịch, trình biên dịch sẽ tự động xác định kiểu của biến đó thông
qua giá trị mà ta gán cho nó lúc khởi tạo.
Ví dụ:
var i = 100;
var s = “C# 3.0″;
Hai dòng lệnh trên tương đương với các dòng lệnh sau:
int i = 100;
4. string s = “C# 3.0″;
Bạn có thể gán cho biến khai báo bằng từ khóa var mọi giá trị mà bạn muốn, từ kiểu
nguyên thủy, kiểu mảng cho đến những kiểu đối tượng phức tạp. Tuy nhiên cần lưu ý là, khi
khai báo một biến với từ khóa var, bạn bắt buộc phải gán giá trị cho nó để trình biên dịch có
thể xác định được biến đó thuộc kiểu gì. Bởi vì var không phải là một kiểu mà chỉ đơn giản
là một từ khóa dùng để khai báo biến. Nếu cố làm điều này, bạn sẽ nhận được lỗi biên dịch
“Implicitly-typed local variables must be initialized”. Cũng thế, việc gán null cho một biến
được khai báo với var là không hợp lệ.
Vậy thì từ khóa var này có lợi ích gì? Chắc hẳn bạn sẽ tự tìm được câu trả lời sau một quá
trình làm việc với nó. Trường hợp đơn giản như khi bạn khai báo một tập hợp có kiểu là
List<int> và sau đó dùng foreach để duyệt qua nó, sau đó bạn lại thay đổi kiểu của tập hợp
này sang List<double>. Khi đó nếu dùng var bạn không cần phải sửa kiểu biến trong cú
pháp lặp nữa. Hơn nữa, mặc dù được khai báo với từ khóa var, bạn vẫn đảm bảo mã lệnh
của mình được tối ưu vì không phải qua bước ép kiểu hoặc boxing nào, và vẫn sử dụng được
chức năng IntelliSense của Visual Studio.
Code:
foreach (var sv in list)
{
Console.WriteLine(sv.Name+”t”+sv.BirthDay.ToShortDateString());
}
Bạn có thể gặp một số trường hợp khác tương tự như vậy. Để thấy rõ hơn công dụng của từ
khóa var, bạn hãy đọc tiếp phần sau.
5. Kiểu dữ liệu nặc danh
(Anonymous types)
Dựa trên khái niệm “Tuple” (một bộ dữ liệu), C# 3.0 cho phép tạo ra một kiểu đối tượng
tạm thời mà không cần định nghĩa trước và khai báo một lớp mới.
Cách sử dụng tương tự như việc tạo một đối tượng thông thường trong C# 3.0 ngoài việc
không cần đến tên lớp. Ở đây ta sẽ thấy được vai trò không thể thiếu của từ khóa var được
giới thiệu ở phần trên.
Code:
var school = new
{
ID = “HUI”,
Name = “ĐH CN”,
Address = “Tp.HCM”
};
Sau khi tạo ra đối tượng school này, ta có thể sử dụng nó tương tự như với các đối tượng
thông thường với 3 property vừa được định nghĩa trong quá trình khởi tạo là ID, Name và
Address.
5. Console.WriteLine(“Address: ” + school.Address);
Console.WriteLine(“Type Name: “+ school.GetType().Name);
Kết quả xuất ra như sau:
Address: Tp.HCM
Type Name: <>f__AnonymousType0`3
Có thể thấy là trình biên dịch tự động tạo ra một class mới với tên theo quy định của nó.
Trong trường hợp bạn tạo ra 2 đối tượng kiểu nặc danh với các thuộc tính giống hệt nhau,
trình biên dịch cũng vẫn chỉ tạo ra duy nhất một class mới. Xem ví dụ sau để hiểu thêm
điều này:
view source
Code:
01 var school1 = new
02 {
03 ID = "HUI",
04 Name = "ĐH CN",
05 Address = "Tp.HCM"
06 };
07 var school2 = new
08 {
09 ID = "HUI",
10 Name = "ĐH CN",
11 Address = "Tp.HCM"
12 };
13
14 Console.WriteLine("Type Name: "+ school1.GetType().Name);
15 Console.WriteLine("Type Name: " + school2.GetType().Name);
Kết quả:
Type Name: <>f__AnonymousType0`3
Type Name: <>f__AnonymousType0`3
6. Kết luận
Những phần được trình bày trên không phải là tất cả những điểm mới về cú pháp trong C#
3.0. Còn những kĩ thuật như Extension Method, Lambd Expression, Expression Tree, Partial
Method,… sẽ được giới thiệu trong mỗi bài viết riêng để truyền tải được phần nào công dụng
và cách thức sử dụng chúng. Tuy nhiên với những chức năng mới trên trong C# 3.0, chắc
hẳn bạn cũng có thể nhận thấy chúng tiện lợi và hiệu quả cho công việc lập trình thế nào.