Tản mạn C#: Hello World

Hầu hết mọi thứ trong C# là đối tượng (object). Do đó, khi động đến C#, ta không thể tránh khỏi làm việc với class. Khái niệm class trong C# vay mượn khá nhiều từ C++. Tuy vậy, C# không mù quáng mà sao chép y nguyên. Những cú pháp rườm rà thường gây ra lỗi trong C++ đã được C# chắt lọc và đơn giản hóa nhằm tăng hiệu quả khi viết code. Nếu so sánh đoạn code định nghĩa class trong C# và C++, ta thấy C# gọn nhẹ hơn, không chứa nhiều chi tiết lặt vặt như C++. Đơn cử là dấu chấm phẩy sau định nghĩa class trong C++ mà tôi rất hay quên. Trong C#, tôi không phải lo về cái dấu nhỏ xíu đó.

Tất cả code C# đều phải nằm trong class hoặc struct. Điều này không bắt buộc trong C++. Cách thức tạo class hoặc struct trong C# rất giống C++.

1
2
3
4
5
6
7
8
9
class Program
{
    ...
}

struct Program
{
    ...
}

Khi tạo project mới trong Visual Studio, file chứa code C# sẽ có phần mở rộng là .cs và có tên trùng với tên class nó chứa. Điều này không bắt buộc, tên file có thể khác tên class và một file có thể chứa nhiều class. Đặc biệt, một class có thể tách ra và nằm ở nhiều file bằng cách dùng từ khóa partial. Thông thường, để dễ quản lý, ta chỉ cần tạo một class hoặc struct nằm trong một file và đặt tên file trùng với tên class hoặc struct đó.

Giống như C++, C# bắt đầu với hàm Main, hay nói đúng hơn là method Main vì hàm nằm trong class được gọi là method. Chữ M của Main được viết hoa, khác với main trong C++.

1
2
3
4
5
6
7
public class Program
{
    public static void Main(string[] args)
    {
        System.Console.WriteLine("Hello World");
    }
}

Từ khóa public cho biết rằng method Main có thể được truy xuất từ bên ngoài phạm vi class chứa nó. Tuy vậy, khi bỏ public thì chương trình vẫn hoạt động bình thường. Có hay không từ khóa public đều không quan trọng với Main, đằng nào nó cũng chạy.

Từ khóa static có nghĩa là Main thuộc class Program chứ không phải thuộc một thực thể (instance) của nó. Do vậy, để gọi Main thì ta không cần tạo một object của class Program. Từ khóa static đối với Main là bắt buộc bởi vì ngay lúc chạy chương trình, chưa có object nào được tạo ra trong bộ nhớ. Do đó, Main phải được truy xuất không qua một object nào cả.

Từ khóa void chỉ ra rằng Main không trả về bất kì giá trị nào. Tuy nhiên, nó có thể trả về giá trị kiểu int nếu muốn (giống C++).

Phần tham số của Main nhận một mảng kiểu string để chứa các thông số dòng lệnh khi chạy chương trình. Phần tham số này không bắt buộc, ta có thể bỏ nếu không dùng đến.

Phần thân của Main nằm giữa hai dấu ngoặc nhọn giống C++. Tương tự, câu lệnh C# cũng kết thúc bằng dấu chấm phẩy. Phần thân của Main rất đơn giản, nó chỉ gồm câu lệnh xuất ra màn hình chữ “Hello world”. Điều cần lưu ý ở đây là chuỗi trong C# phải nằm trên cùng một dòng, nếu muốn nối nhiều chuỗi riêng biệt với nhau thì có thể dùng toán tử + quen thuộc.

Chương trình của ta không thực hiện chức năng gì phức tạp, nó gọi duy nhất một method tên System.Console.WriteLine(). Trong đó, System là tên của namespace chứa class Console và class Console chứa method WriteLine(). Nếu tra cứu trong MSDN thì ta thấy WriteLine có khá nhiều phiên bản khác nhau, ta gọi chúng là các “quá tải” (overload). Quan sát kỹ, ta thấy WriteLine() được khai báo static tương tự Main. Điều này nghĩa là ta chỉ có thể gọi method thông qua tên class chứa nó, trong trường hợp này là class Console, và ta phân cách tên class và tên method bằng dấu chấm (thay vì phải dùng dấu :: như trong C++). Chuyển qua trang thông tin về class Console trên MSDN, ta phát hiện ra class Console nằm trong một file DLL tên là mscorlib.dll. Đây là file assembly chứa code cho class Console. File mscorlib.dll có vai trò rất quan trọng trong .NET vì nó chứa tất cả các class và struct căn bản mà hầu hết các chương trình .NET đều sử dụng.

Khi biên dịch chương trình C++, ta thường sử dụng chỉ thị #include ở đầu file để tham chiếu đến các file header. File header cung cấp prototype cho các hàm trong file. Trình biên dịch C# không cần file header. Trong quá trình biên dịch, trình biên dịch C# truy cập trực tiếp file mscorlib.dll và lấy thông tin cần thiết từ metadata trong đó. Tiếp theo, trình biên dịch xác định xem câu lệnh gọi hàm WriteLine() có hợp lệ không, nếu có thì trình biên dịch sẽ thiết lập một tham chiếu đến file mscorlib.dll trong file thực thi.

Nếu dùng Visual Studio để viết code, tính năng nhắc mã Intellisense sẽ liên tục hỗ trợ ta bằng cách gợi ý tên namespace, tên kiểu, tên method… mà nó tìm thấy trong các file dll.

File mscorlib.dll là file mặc định mà trình biên dịch C# sẽ truy xuất. Đối với những file DLL khác, ta phải tạo tham chiếu đến chúng. Trong Visual Studio, ta click phải vào mục References trong Solution Explorer, sau đó chọn Add Reference… và chọn file DLL cần tham chiếu.

Việc gõ System.Console.WriteLine() trở nên rườm rà không cần thiết. Do đó, ta viết lại đoạn code trên như sau:

1
2
3
4
5
6
7
8
9
using System;

public class Program
{
    public static void Main()
    {
        Console.WriteLine("Hello World");
    }
}

Lệnh using ở dòng 1 dùng để thông báo với trình biên dịch C# rằng nếu nó không tìm được Console.WriteLine() thì nên tìm trong namespace System. Ta cần lưu ý là lệnh using trong C# không giống như #include trong C++. using không tham chiếu bất kì file nào trong hệ thống. Nó chỉ khai báo tên của namespace cần dùng để sau này ta không phải viết lại, giúp tiết kiệm thời gian đánh máy, đồng thời code cũng trở nên sáng sủa và gọn gàng.