Tài liệu C#
Tài liệu tham khảo C# ngắn gọn, đi kèm ví dụ. Đọc khái niệm, xem code, rồi luyện tập trong một hành trình Coddy.
Bắt đầu khóa họcBắt đầu
- C# là gìC# là ngôn ngữ đa dụng, định kiểu tĩnh của Microsoft, chạy trên .NET. Bài này giải thích C# dùng để làm gì, liên quan thế nào với .NET, mỗi phiên bản thêm gì và một chương trình C# được tổ chức ra sao.
- Hello WorldChương trình C# đầu tiên của bạn, giải thích từng dòng: phương thức Main, Console.WriteLine và Write, đọc đầu vào bằng Console.ReadLine, chú thích, tạo và chạy project bằng dotnet CLI, và top-level statements.
- C# vs JavaC# và Java trông giống nhau và giải quyết những bài toán tương tự. Khác biệt thật sự nằm ở property, kiểu giá trị, generic, LINQ và stream, async, ngoại lệ và hệ sinh thái xung quanh. So sánh song song kèm code.
- C# vs C++C# là ngôn ngữ managed có bộ thu gom rác; C++ biên dịch ra mã máy và cho bạn tự quản lý bộ nhớ. Bài này cho thấy khác biệt đó ảnh hưởng thế nào đến độ an toàn, hiệu năng, game engine, mô hình build và độ khó khi học.
- Namespace và usingCách namespace trong C# tổ chức các kiểu và cách chỉ thị using import chúng: alias, using static, tên đầy đủ, sửa lỗi tham chiếu mơ hồ, global using và file-scoped namespace.
Kiến thức cơ bản
- BiếnCách khai báo và gán giá trị cho biến trong C#, khi nào nên và không nên dùng var, phạm vi hoạt động ra sao, khác biệt giữa const, readonly và static readonly, quy ước đặt tên, và lỗi dùng biến chưa được gán giá trị.
- Kiểu dữ liệuMọi kiểu có sẵn của C# cùng kích thước, phạm vi và hậu tố literal, khác biệt giữa kiểu giá trị và kiểu tham chiếu, vì sao decimal là kiểu dành cho tiền, tràn số nguyên và checked, và giá trị mặc định.
- Chuyển string sang intCách chuyển một chuỗi sang int trong C# bằng int.Parse, int.TryParse và Convert.ToInt32, cách ép kiểu ngầm định và tường minh hoạt động, vì sao ép một double thì phần thập phân bị cắt bỏ, toán tử as và is, và vì sao "1.5" có thể được parse thành 15.
- Toán tửCác toán tử C# và cách chúng hoạt động chính xác: chia số nguyên, toán tử chia lấy dư với số âm, tăng giảm và gán kết hợp, so sánh, logic ngắt mạch, toán tử bit, các toán tử null và bảng độ ưu tiên.
- ChuỗiCách chuỗi hoạt động trong C#: tính bất biến, Length và chỉ số, so sánh bằng ==, Equals và string.Compare, ký tự escape, chuỗi verbatim @ và văn bản nhiều dòng, raw string literal, kiểm tra null và rỗng, duyệt qua từng ký tự.
- SubstringCách String.Substring hoạt động trong C#: đối số chỉ số bắt đầu và độ dài, ngoại lệ ArgumentOutOfRangeException và cách tránh, cắt văn bản quanh kết quả của IndexOf, lấy n ký tự đầu hoặc cuối, và toán tử range từ C# 8.
- Nội suy chuỗiCách nội suy chuỗi $"..." hoạt động trong C#: biểu thức trong dấu ngoặc nhọn, format specifier như F2, N0, C, D5 và X, căn lề cho bảng, thoát dấu ngoặc nhọn, string.Format và composite formatting, chuỗi nội suy verbatim và raw, và định dạng theo một culture cụ thể.
- Phương thức chuỗiNhững phương thức String bạn dùng hằng ngày trong C#: Split với nhiều dấu phân cách và tùy chọn, Replace, Contains và tìm kiếm không phân biệt hoa thường, IndexOf và LastIndexOf, StartsWith và EndsWith, Trim, ToUpper và ToLower, PadLeft, Join, Concat và đảo ngược chuỗi.
- StringBuilderVì sao nối chuỗi lặp đi lặp lại trong vòng lặp lại chậm trong C#, và StringBuilder khắc phục điều đó thế nào: Append, AppendLine, AppendFormat, Insert, Remove, Replace, capacity, và khi nào nối chuỗi thông thường hoặc string.Join là lựa chọn tốt hơn.
- Số ngẫu nhiênCách tạo số ngẫu nhiên trong C# với lớp Random: Next và cận trên không bao gồm, số thực ngẫu nhiên trong một khoảng, seed để có kết quả lặp lại được, chọn một phần tử ngẫu nhiên, trộn danh sách, cái bẫy new Random trong vòng lặp, và số ngẫu nhiên an toàn về mật mã.
- DateTimeLàm việc với ngày giờ trong C#: tạo giá trị DateTime, Now, UtcNow và Today, cộng ngày và tháng, trừ để có TimeSpan, TotalHours và Hours, so sánh ngày, DayOfWeek, phân tích chuỗi bằng ParseExact và TryParse, DateTimeOffset và DateOnly.
- Định dạng DateTimeCách định dạng DateTime thành chuỗi trong C#: các chuỗi định dạng chuẩn (d, D, o, s, u, t), mẫu tùy chỉnh như yyyy-MM-dd HH:mm:ss, bảng đầy đủ các ký hiệu định dạng, culture thay đổi đầu ra thế nào, ISO 8601 round-trip với "o", và các lỗi nhầm mm với MM, hh với HH.
Luồng điều khiển
- if elseCách if, else if và else hoạt động trong C#: vì sao điều kiện phải là bool, kết hợp điều kiện bằng &&, || và !, khi nào cặp ngoặc nhọn quan trọng, guard clause thay cho lồng nhau sâu, và các lỗi nhầm = với == hay dấu chấm phẩy thừa.
- Câu lệnh switchCách câu lệnh switch hoạt động trong C#: switch trên giá trị int, string, char và enum, vì sao mọi case cần break (lỗi CS0163), xếp chồng nhãn case cho nhiều giá trị, goto case, default, phân biệt hoa thường với chuỗi, và nhãn case dạng pattern với when.
- Switch expressionSwitch expression của C# 8 biến một giá trị thành kết quả bằng các nhánh pattern thay vì case và break. Cú pháp, discard _, pattern thuộc tính, tuple và so sánh, and/or/not, điều kiện when, cảnh báo tính đầy đủ và SwitchExpressionException, kèm các cách viết tương đương chạy được trên C# 7.
- Toán tử ba ngôiToán tử điều kiện condition ? a : b trong C# chọn một trong hai giá trị. Cách nó được tính, vì sao hai nhánh cần một kiểu chung (CS0173) và phép ép kiểu để sửa lỗi, toán tử ba ngôi lồng nhau, dấu ngoặc trong nội suy chuỗi, và khi nào ?? hay ?. diễn đạt tốt hơn.
- Vòng lặp forCách vòng lặp for trong C# hoạt động: phần khởi tạo, điều kiện và bước lặp, đếm ngược và bước nhảy, nhiều biến lặp, duyệt mảng theo chỉ số, vòng lặp lồng nhau, for(;;), cùng các lỗi lệch một đơn vị và xóa phần tử khi đang lặp.
- Vòng lặp foreachCách foreach hoạt động trong C#: duyệt mảng, list, chuỗi và từ điển (KeyValuePair và deconstruction), lấy chỉ số, vì sao biến lặp chỉ đọc, ngoại lệ InvalidOperationException "Collection was modified" và cách khắc phục, và một kiểu cần gì để dùng được trong foreach.
- Vòng lặp whileCách vòng lặp while và do-while hoạt động trong C#: điều kiện được kiểm tra trước hay sau thân vòng lặp, đọc input đến khi gặp từ dừng hoặc hết input, while (true) với break, và những lỗi vòng lặp vô hạn hay gặp.
- break và continueCách break, continue và goto hoạt động trong C#: kết thúc vòng lặp sớm, bỏ qua một lần lặp, break bên trong switch, ba cách thoát khỏi vòng lặp lồng nhau (biến cờ, goto, hoặc phương thức với return), và bug vòng lặp vô hạn do continue trong while.
Phương thức và delegate
- Phương thứcCách khai báo và gọi phương thức trong C#: kiểu trả về và void, tham số, phương thức dạng biểu thức, trả về nhiều giá trị bằng tuple, overload, mảng params, phương thức static và instance, phương thức Main, đệ quy và hàm cục bộ.
- Tham số tùy chọnCách tham số tùy chọn và đối số có tên hoạt động trong C#: giá trị mặc định và quy tắc hằng số lúc biên dịch, thứ tự tham số, bỏ qua đối số bằng tên, tham số tùy chọn so với nạp chồng, các thuộc tính caller info và cái bẫy phiên bản khi giá trị mặc định được ghi sẵn vào nơi gọi.
- ref và outCách tham số ref, out và in hoạt động trong C#: truyền chính biến thay vì bản sao, out cho kết quả phụ và mẫu TryParse, out var, tham số in chỉ đọc, quy tắc gán giá trị bắt buộc, và điều gì thay đổi khi đối số là kiểu tham chiếu.
- Biểu thức lambdaBiểu thức lambda trong C# hoạt động thế nào: cú pháp => ở mọi dạng, lưu lambda vào Func và Action, truyền lambda cho phương thức và LINQ, statement lambda, closure với biến được capture, và bug capture trong vòng lặp for mà foreach không mắc phải.
- DelegateDelegate trong C# là gì và cách dùng: khai báo kiểu delegate, tạo delegate từ phương thức, lambda hoặc phương thức ẩn danh, gọi delegate, delegate làm callback, multicast delegate với += và -=, kiểm tra null bằng ?.Invoke, và quan hệ giữa delegate với Func, Action và event.
- Func và ActionCách Func và Action hoạt động trong C#: Func<T, TResult> cho hàm trả về giá trị, Action<T> cho hàm không trả về, Predicate và Comparison, truyền và trả về hàm, từ điển lệnh, lambda async, và khi nào nên tự khai báo delegate riêng.
- EventCách event hoạt động trong C#: khai báo event với EventHandler và EventHandler<T>, lớp EventArgs tự định nghĩa, đăng ký và hủy đăng ký bằng += và -=, kích hoạt event an toàn với ?.Invoke, vì sao event tốt hơn một trường delegate public, và rò rỉ bộ nhớ do quên hủy đăng ký.
- Extension methodCách extension method hoạt động trong C#: một phương thức static trong lớp static với this ở tham số đầu tiên, gọi như phương thức instance, mở rộng string, enum và IEnumerable<T>, LINQ được xây dựng từ chúng ra sao, vì sao phương thức instance được ưu tiên, receiver null và chỉ thị using cần có.
Collection và LINQ
- MảngMảng trong C# là một khối phần tử cùng kiểu với kích thước cố định. Tìm hiểu cách khai báo và khởi tạo mảng, đọc Length, sắp xếp và tìm kiếm bằng lớp Array, sao chép mảng an toàn, và làm việc với mảng 2 chiều và jagged array.
- ListList<T> là mảng có thể co giãn của C#. Tìm hiểu cách tạo list, thêm và chèn phần tử, xóa theo giá trị, chỉ số hoặc điều kiện, tìm kiếm bằng Contains và Find, sắp xếp theo thuộc tính, và tránh lỗi khi thay đổi list bên trong foreach.
- DictionaryDictionary<TKey, TValue> ánh xạ khóa sang giá trị và tra cứu rất nhanh. Học cách thêm và cập nhật phần tử, đọc an toàn bằng TryGetValue, duyệt các KeyValuePair, đếm số lần xuất hiện, bỏ qua chữ hoa chữ thường ở khóa, và giữ khóa luôn được sắp xếp với SortedDictionary.
- HashSetHashSet<T> chứa các phần tử không trùng nhau và trả lời Contains trong thời gian hằng số. Tìm hiểu cách Add báo phần tử trùng, cách loại bỏ trùng lặp trong list, cách hợp, giao và trừ tập hợp, và cách để set so sánh đối tượng của bạn theo giá trị.
- QueueQueue<T> là collection vào trước ra trước: phần tử rời hàng đợi theo đúng thứ tự chúng đến. Tìm hiểu Enqueue, Dequeue và Peek, ngoại lệ khi hàng đợi rỗng và TryDequeue, tìm kiếm theo chiều rộng bằng hàng đợi, và khi nào nên dùng ConcurrentQueue hay PriorityQueue.
- StackStack<T> là collection vào sau, ra trước: phần tử được thêm gần nhất sẽ ra đầu tiên. Tìm hiểu Push, Pop và Peek, ngoại lệ khi stack rỗng và TryPop, vì sao stack được duyệt theo thứ tự ngược, cùng hai ứng dụng kinh điển: lịch sử hoàn tác và kiểm tra dấu ngoặc cân bằng.
- TupleTuple trong C# gom vài giá trị thành một mà không cần khai báo kiểu. Tìm hiểu cú pháp value tuple, đặt tên phần tử, trả về nhiều giá trị từ một phương thức, deconstruction với var (a, b), dùng tuple làm khóa của dictionary, và ValueTuple khác System.Tuple cũ ra sao.
- LINQLINQ bổ sung các toán tử truy vấn cho mọi collection trong C#. Tìm hiểu method syntax và query syntax, lọc bằng Where, chiếu bằng Select, sắp xếp bằng OrderBy và ThenBy, nhóm bằng GroupBy, cái bẫy First và FirstOrDefault, các hàm tổng hợp, và cách thực thi trì hoãn thay đổi thời điểm truy vấn chạy.
- IEnumerable và yieldIEnumerable<T> là interface đứng sau foreach và LINQ, còn yield return là cách dễ nhất để triển khai nó. Tìm hiểu IEnumerable liên quan thế nào tới ICollection và List, một phương thức iterator chạy từng bước ra sao, yield break, chuỗi vô hạn và những cạm bẫy của đánh giá lazy.
Lớp và kiểu dữ liệu
- Lớp và đối tượngCách khai báo một lớp trong C#, thêm trường và phương thức, tạo đối tượng bằng new, và ý nghĩa của việc lớp là kiểu tham chiếu: đối tượng dùng chung, null và NullReferenceException, ToString và object initializer.
- Hàm khởi tạoCách hàm khởi tạo (constructor) hoạt động trong C#: hàm khởi tạo mặc định ngầm định và khi nào nó biến mất, hàm khởi tạo có tham số và nạp chồng, gọi nối bằng this(...) và base(...), thứ tự thực thi, hàm khởi tạo static và private, và primary constructor của C# 12.
- Thuộc tínhCách thuộc tính trong C# hoạt động: get và set bao quanh một trường lưu trữ, auto-property, private set và thuộc tính chỉ đọc, thuộc tính tính toán, kiểm tra dữ liệu trong setter, cùng các từ khóa init và required từ C# 9 và 11.
- Từ khóa staticstatic nghĩa là gì trong C#: thành viên thuộc về kiểu thay vì một đối tượng, trạng thái dùng chung giữa các instance, phương thức tĩnh và lớp tĩnh, hàm khởi tạo tĩnh, const và static readonly, using static, và lỗi CS0120.
- Access modifierSáu access modifier trong C# (public, private, protected, internal, protected internal, private protected), mỗi loại cho phép những gì, giá trị mặc định khi bạn không ghi gì, và các lỗi biên dịch chúng gây ra.
- Kế thừaCách kế thừa lớp hoạt động trong C#: kế thừa bằng dấu hai chấm, những gì được và không được kế thừa, gọi lớp cơ sở bằng base, virtual và override, che giấu bằng new (và đầu ra khiến ai cũng bất ngờ), sealed, đa hình, và ép kiểu lên xuống trong cây kế thừa.
- Lớp trừu tượngLớp trừu tượng (abstract class) trong C# là gì: một lớp cơ sở không thể tạo instance, có thể khai báo các thành viên abstract mà lớp dẫn xuất bắt buộc phải cài đặt. Bài viết đề cập phương thức và thuộc tính abstract, hàm khởi tạo, mẫu template method và khi nào nên chọn interface.
- InterfaceCách interface hoạt động trong C#: khai báo interface, triển khai nó trong lớp và struct, dùng interface như một kiểu, triển khai nhiều interface cùng lúc, triển khai tường minh, các interface của framework bạn sẽ triển khai nhiều nhất (IComparable<T>, IEnumerable<T>, IDisposable), và phương thức mặc định của interface trong C# 8.
- StructCách struct hoạt động trong C#: ngữ nghĩa giá trị và sao chép khi gán, lỗi khi sửa phần tử List<T>, quy tắc hàm khởi tạo, so sánh bằng, boxing, readonly struct và record struct, cùng bảng so sánh struct và class kèm lời khuyên khi nào struct là lựa chọn đúng.
- EnumCách enum hoạt động trong C#: khai báo các hằng số có tên, giá trị số nguyên bên dưới và ép kiểu, chuyển enum sang chuỗi và chuỗi sang enum bằng Parse và TryParse, liệt kê mọi giá trị, [Flags] với toán tử bit và HasFlag, dùng switch với enum và xử lý giá trị không xác định.
- RecordRecord trong C# là gì (từ C# 9): cú pháp vị trí, các thành viên do trình biên dịch sinh ra, so sánh bằng theo giá trị, sao chép không phá hủy bằng with, ToString có sẵn, record struct từ C# 10, kế thừa giữa các record, và lớp tương đương viết tay.
- GenericsCách generics hoạt động trong C#: viết lớp và phương thức generic với tham số kiểu, suy luận kiểu, ràng buộc bằng where (class, struct, new(), lớp cơ sở, interface), default(T), thành viên static theo từng kiểu và hiệp biến với IEnumerable<out T>.
- Kiểu nullableMọi điều về null trong C#: kiểu giá trị nullable (int?, Nullable<T>) với HasValue, Value và GetValueOrDefault, các toán tử null-coalescing ?? và ??=, toán tử null-conditional ?., cách kiểm tra null và kiểu tham chiếu nullable (string?) từ C# 8.
- Pattern matchingCách pattern matching hoạt động trong C#: toán tử is với mẫu kiểu và mẫu hằng, câu lệnh switch với mẫu trong case và when, biểu thức switch, mẫu thuộc tính, mẫu tuple và mẫu vị trí, mẫu quan hệ và mẫu logic (and, or, not), mẫu danh sách, và phiên bản C# nào bổ sung từng loại.
Ngoại lệ
- Try Catchtry, catch và finally là cách C# xử lý ngoại lệ. Tìm hiểu cách một ngoại lệ đi ngược lên call stack, cách bắt từng kiểu ngoại lệ cụ thể theo đúng thứ tự, lọc bằng when, dọn dẹp trong finally, ném lại bằng throw; mà không mất stack trace, và những ngoại lệ không nên bắt.
- Ném ngoại lệCách và thời điểm ném ngoại lệ trong C#. Tìm hiểu câu lệnh throw, kiểu ngoại lệ có sẵn nào hợp với lỗi nào, throw expression với ?? và ?:, các helper ThrowIfNull, và cách viết lớp ngoại lệ tự tạo có dữ liệu riêng và inner exception.
- Câu lệnh usingCâu lệnh using bảo đảm Dispose được gọi trên tệp, stream, kết nối và các đối tượng IDisposable khác, kể cả khi có ngoại lệ được ném ra. Tìm hiểu nó được biên dịch thành gì, thứ tự các đối tượng được dispose, using declaration của C# 8, và cách cài đặt IDisposable trong lớp của bạn.
Bất đồng bộ và luồng
- Async awaitasync và await giúp code C# chờ các thao tác chậm mà không chặn thread. Tìm hiểu cách một phương thức async chạy, Task và Task<T>, chạy đồng thời các thao tác độc lập với Task.WhenAll, ngoại lệ trong code async, và vì sao async void và .Result gây ra bug và deadlock.
- TaskTask là đơn vị công việc bất đồng bộ và song song trong .NET. Tìm hiểu Task.Run cho công việc nặng về CPU, đọc kết quả, kết hợp task bằng WhenAll và WhenAny, continuation, hủy bằng CancellationToken, Task khác Thread ra sao, và khi nào Parallel.For phù hợp hơn.
- LockCâu lệnh lock chỉ cho một luồng chạy một khối code tại mỗi thời điểm. Xem race condition làm sai một bộ đếm, sửa bằng lock, chọn đúng đối tượng để khóa, dùng Interlocked cho bộ đếm đơn giản, tránh deadlock do thứ tự khóa, và dùng SemaphoreSlim khi code bên trong có await.
- TimerC# có nhiều lớp timer và chúng hoạt động khác nhau. Tìm hiểu khi nào dùng System.Threading.Timer, System.Timers.Timer, PeriodicTimer hay timer của giao diện, cách bắt đầu và dừng chúng, vì sao callback chạy trên thread pool và có thể chồng lên nhau, vì sao phải giữ tham chiếu tới timer, và cách đo thời gian chạy bằng Stopwatch.
Tệp, JSON và nhiều hơn nữa
- Đọc và ghi tệpCách ghi và đọc tệp trong C#: File.WriteAllText, ReadAllText, AppendAllText, WriteAllLines và ReadAllLines cho cả tệp, StreamWriter và StreamReader cho tệp lớn, đường dẫn với Path.Combine, thư mục, mã hóa ký tự và các ngoại lệ mà code xử lý tệp phải lường trước.
- JSONCách làm việc với JSON trong C# bằng System.Text.Json: JsonSerializer.Serialize và Deserialize, đầu ra camelCase và thụt lề, các attribute như JsonPropertyName và JsonIgnore, enum dạng chuỗi, đọc JSON không cần lớp qua JsonDocument và JsonNode, xử lý lỗi, và so sánh với Newtonsoft.Json.
- RegexCách dùng biểu thức chính quy trong C# với System.Text.RegularExpressions: IsMatch, Match và Matches, group đánh số và group có tên, Replace với chuỗi thay thế và lambda, Split, RegexOptions, chuỗi verbatim cho pattern, kiểm tra đầu vào, timeout và attribute GeneratedRegex của .NET 7.
- Reflection và attributeCách reflection hoạt động trong C#: đối tượng Type từ typeof và GetType, đọc và gán thuộc tính, gọi phương thức theo tên, tạo instance, và attribute: các attribute có sẵn như Obsolete, cách tự khai báo và đọc chúng lúc chạy. Kèm cái giá của reflection.