Named and optional parameters

C# 4.0 NETFx 4.5

Published Updated Author Jeffrey T. Fritz Reading time

Named arguments and default values make APIs easier to read without multiplying overloads.

C# 4.0 added two small features that show up everywhere in modern code: named arguments and optional parameters. Together they make method calls self-documenting and help library authors avoid overload explosions for straightforward customization points.

They work especially well for logging, formatting, configuration, and interop-heavy APIs where the method signature is stable but most callers use only a couple of non-default settings.

Why it matters

Named arguments improve call-site clarity when positional arguments start to blur together, especially with multiple bool, string, or numeric values. Optional parameters let you expose sensible defaults once instead of maintaining a ladder of near-duplicate overloads.

That combination is a good fit for utility methods, client SDKs, and formatting helpers where most callers follow the common path but a few need to opt into extra behavior.

Cautions

Optional parameter defaults are baked in at the call site at compile time. If you ship a library and later change a default value, older compiled callers keep using the original value until they are rebuilt.

Also avoid turning every knob into an optional parameter. When a method grows too many options, a dedicated options object or builder usually scales better and reads more clearly.

Make multi-flag calls readable

Named arguments remove guesswork when a method has several parameters of the same shape or a few optional switches.

Valid since C# 4.0

Without var

using System;

public sealed class NotificationService
{
    public void Send(string recipient, string subject, string body, bool highPriority, bool saveCopy)
    {
        Console.WriteLine("To: " + recipient);
        Console.WriteLine("Subject: " + subject);
        Console.WriteLine(
            string.Format(
                "Priority: {0} | Save copy: {1}",
                highPriority ? "high" : "normal",
                saveCopy));
        Console.WriteLine(body);
    }
}

public static class Program
{
    public static void Main()
    {
        var service = new NotificationService();
        service.Send(
            "team@contoso.com",
            "Deployment complete",
            "The new release is live.",
            true,
            false);
    }
}

With var

using System;

public sealed class NotificationService
{
    public void Send(
        string recipient,
        string subject,
        string body,
        bool highPriority = false,
        bool saveCopy = true)
    {
        Console.WriteLine("To: " + recipient);
        Console.WriteLine("Subject: " + subject);
        Console.WriteLine(
            string.Format(
                "Priority: {0} | Save copy: {1}",
                highPriority ? "high" : "normal",
                saveCopy));
        Console.WriteLine(body);
    }
}

public static class Program
{
    public static void Main()
    {
        var service = new NotificationService();
        service.Send(
            recipient: "team@contoso.com",
            subject: "Deployment complete",
            body: "The new release is live.",
            highPriority: true,
            saveCopy: false);
    }
}

Trim overloads with optional defaults

Optional parameters let one method cover the common path while still allowing targeted customization.

Valid since C# 4.0

using System;

public static class InvoiceFormatter
{
    public static string FormatInvoice(
        string customerName,
        decimal amount,
        string currency = "USD",
        bool includeDueDate = false)
    {
        var message = string.Format(
            "Invoice for {0}: {1:0.00} {2}",
            customerName,
            amount,
            currency);

        return includeDueDate
            ? message + " (due in 14 days)"
            : message;
    }
}

public static class Program
{
    public static void Main()
    {
        Console.WriteLine(InvoiceFormatter.FormatInvoice("A. Datum", 1200m));
        Console.WriteLine(
            InvoiceFormatter.FormatInvoice(
                "A. Datum",
                1200m,
                currency: "EUR",
                includeDueDate: true));
    }
}

Learn more

Named and optional arguments (Microsoft Learn)