Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C#

Tham số tùy chọn (Optional Parameters) trong C#: Default Value và Optional Attribute

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

  1. Optional parameters PHẢI đứng sau required parameters.
  2. Không được: void M(int a = 1, int b) → Compile Error: "Optional parameters must appear after all required parameters"
  3. Đú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ểmOptional ParametersMethod Overloading
Số lượng method1 methodNhiều method
Default valueTại định nghĩaTại body method overload
Binary compatibilityThay đổi default = breaking change (cần recompile caller)An toàn hơn (thêm overload mới)
ReflectionXem được default value qua ParameterInfo.DefaultValueKhông áp dụng
Optional attributeCó 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