Khái niệm
Mặc định tất cả tham số method đều bắt buộc (required). Optional Parameters cho phép gọi method không truyền đối số cho tham số đó - sẽ dùng giá trị mặc định được định nghĩa trong signature.
Cách 1: Default Value (Khuyến nghị, C# 4.0+)
public void Greet(string name, string greeting = "Hello")
{
Console.WriteLine($"{greeting}, {name}!");
}
// Gọi
Greet("John"); // "Hello, John!"
Greet("John", "Hi"); // "Hi, John!"
Greet("John", greeting: "Hey"); // Named parameter
Cách 2: [Optional] Attribute (COM Interop, Legacy)
using System.Runtime.InteropServices;
public void Greet(string name, [Optional] string greeting)
{
// greeting sẽ là null nếu không truyền (KHÔNG có default value tự động!)
Console.WriteLine($"{(greeting ?? "Hello")}, {name}!");
}
⚠️ Quan trọng:
[Optional] KHÔNG cung cấp default value. Chỉ báo cho compiler biết tham số có thể bỏ qua. Giá trị sẽ là default(T) (null cho reference type, 0 cho int, false cho bool...). Luôn ưu tiên Default Value.
Quy tắc bắt buộc
- Optional parameters PHẢI đứng sau required parameters.
- Không được:
void M(int a = 1, int b)→ Compile Error: "Optional parameters must appear after all required parameters" - Đúng:
void M(int a, int b = 1)
Ví dụ sai và đúng
// ❌ SAI - Optional trước required
public void Wrong(int a = 5, int b) { } // Compile Error!
// ✅ ĐÚNG - Required trước, optional sau
public void Correct(int a, int b = 5) { }
// ✅ ĐÚNG - Tất cả optional
public void AllOptional(int a = 1, int b = 2, string c = "default") { }
Ví dụ thực tế
public class EmailService
{
public void Send(
string to,
string subject,
string body,
bool isHtml = false,
string cc = "",
string bcc = "",
int priority = 3, // 1=High, 3=Normal, 5=Low
CancellationToken cancellationToken = default
)
{
// Implementation
}
}
// Gọi linh hoạt
email.Send("user@test.com", "Welcome", "Body");
email.Send("user@test.com", "Welcome", "Body", isHtml: true);
email.Send("user@test.com", "Welcome", "Body", priority: 1, cc: "manager@test.com");
email.Send(to: "user@test.com", subject: "Hi", body: "Body", bcc: "archive@test.com");
Optional Parameters vs Method Overloading
| Đặc điểm | Optional Parameters | Method Overloading |
|---|---|---|
| Số lượng method | 1 method | Nhiều method |
| Default value | Tại định nghĩa | Tại body method overload |
| Binary compatibility | Thay đổi default = breaking change (cần recompile caller) | An toàn hơn (thêm overload mới) |
| Reflection | Xem được default value qua ParameterInfo.DefaultValue | Không áp dụng |
| Optional attribute | Có thể dùng [Optional] | Không cần |
Lưu ý quan trọng về Binary Compatibility
⚠️ Breaking Change: Nếu thay đổi default value của optional parameter, caller phải recompile để nhận giá trị mới. Nếu chỉ deploy DLL mới mà không rebuild caller → caller vẫn dùng default value cũ (được embed vào IL của caller tại compile time).
// Library v1.0
public void Connect(string server, int timeout = 30) { }
// Caller compile với v1.0 → IL chứa: Connect("db", 30)
// Library v2.0 - đổi default
public void Connect(string server, int timeout = 60) { }
// Caller KHÔNG recompile → vẫn gọi Connect("db", 30) thay vì 60!
Giải pháp cho Library Authors
- Dùng Method Overloading cho public API library.
- Hoặc dùng Options Pattern (class config):
Connect(server, new ConnectOptions { Timeout = 60 }). - Reserved parameters:
void M(int a, int reserved = 0)- để mở rộng sau này.
C# 12: Primary Constructors & Default Parameters
// C# 12 - Primary constructor với default
public class Service(string name, int timeout = 30, bool retry = true)
{
public void Run() => Console.WriteLine($"{name}: timeout={timeout}, retry={retry}");
}
var s = new Service("API"); // timeout=30, retry=true