Menu

Parámetros opcionales y argumentos con nombre en C#: reglas

Cómo funcionan los parámetros opcionales y los argumentos con nombre en C#: valores por defecto y la regla de la constante en tiempo de compilación, el orden de los parámetros, saltarse argumentos por nombre, parámetros opcionales frente a sobrecargas, atributos de información del llamador y la trampa de versiones de los valores por defecto grabados en quien llama.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Un parámetro opcional tiene un valor por defecto en la declaración del método, así que quien llama puede omitirlo. Un argumento con nombre pasa un valor por el nombre del parámetro en lugar de por su posición. Juntos permiten que un solo método sirva para muchas formas de llamada sin un montón de sobrecargas.

Salida:

to ana@mail.com: (no subject), retries 3
to ben@mail.com: Invoice #1042, retries 3
to cy@mail.com: (no subject) [URGENT], retries 3
to dev@mail.com: Build failed, retries 0

La tercera llamada se salta subject y asigna urgent por nombre; sin argumentos con nombre tendría que volver a pasar "(no subject)" solo para llegar a la tercera posición. La última llamada pasa todos los argumentos por nombre en un orden distinto al de la declaración, lo cual es válido.

Reglas para los valores por defecto

Un valor por defecto debe ser algo que el compilador pueda calcular:

  • una constante (3, "INFO", true, 1.5m, un campo const, un miembro de un enum)
  • null para un tipo de referencia o nullable
  • default(T), o new T() para un tipo de valor T

Todo lo que se evalúa en tiempo de ejecución se rechaza. El caso clásico es una fecha:

static void Schedule(string task, DateTime at = DateTime.Now) { }
// error CS1736: Default parameter value for 'at' must be a compile-time constant

La solución habitual es un parámetro nullable con null como valor por defecto, resuelto en el cuerpo:

Salida:

backup at 2026-01-01 09:00, tags: 0
report at 2026-03-15 18:30, tags: 0
deploy at 2026-01-01 09:00, tags: 2

El mismo truco sirve para colecciones: un valor por defecto new List<string>() no es una constante, así que usa null por defecto y crea la lista dentro. Eso también evita el bug de Python del valor por defecto mutable compartido, que C# descarta al permitir solo constantes.

Reglas de orden

Primero van los parámetros obligatorios, después los opcionales y al final un array params (si lo hay):

static void Log(string message, string level = "INFO", params string[] tags) { }   // OK

static void Log(string level = "INFO", string message) { }
// error CS1737: Optional parameters must appear after all required parameters

Los parámetros ref y out no pueden ser opcionales.

En la llamada, los argumentos posicionales rellenan los parámetros desde el principio. Los argumentos con nombre pueden seguirlos en cualquier orden. Desde C# 7.2, un argumento con nombre también puede aparecer antes de uno posicional, pero solo cuando está en su propia posición (SendEmail("a@b.c", subject: "Hi", true)); en versiones anteriores, los argumentos con nombre deben ir todos al final. Omitir un parámetro obligatorio, aunque se nombren otros, es un error de compilación.

Argumentos con nombre para la legibilidad

Los argumentos con nombre son útiles incluso cuando nada es opcional. Los literales true, false, null y los números sueltos no dicen nada en el punto de llamada:

ResizeImage(photo, 800, 600, true, false);                                   // which is which?
ResizeImage(photo, width: 800, height: 600, keepAspect: true, upscale: false);

Renombrar un parámetro pasa a ser un cambio incompatible para quienes llaman usando el nombre, algo que conviene recordar en una biblioteca pública.

Parámetros opcionales frente a sobrecargas

Antes de C# 4, la misma flexibilidad necesitaba una sobrecarga por combinación. Los parámetros opcionales las reducen a un solo método:

// overloads
static void Connect(string host) => Connect(host, 443);
static void Connect(string host, int port) => Connect(host, port, 30);
static void Connect(string host, int port, int timeoutSeconds) { /* ... */ }

// one method with optional parameters
static void Connect(string host, int port = 443, int timeoutSeconds = 30) { /* ... */ }

Cuando existen las dos cosas, la resolución de sobrecargas prefiere un candidato que no necesite rellenar ningún valor por defecto:

Salida:

Greet()
Greet(string) with Lena

Greet() coincide con los dos métodos, y el compilador elige el que no tiene un parámetro opcional omitido. Mezclar las dos técnicas sobre el mismo nombre suele producir llamadas cuyo destino es difícil de prever, así que elige una por método.

Elige sobrecargas cuando las variantes necesitan código distinto o tipos de parámetro distintos, y parámetros opcionales cuando solo se diferencian en los valores por defecto.

Los valores por defecto se graban en quien llama

Un valor por defecto no se consulta en tiempo de ejecución. El compilador lo copia en cada punto de llamada cuando se compila el código que llama. Connect("api.shop.com") se compila como Connect("api.shop.com", 443, 30).

Eso tiene una consecuencia para las bibliotecas. Supón que la versión 1 de un paquete publica Connect(string host, int timeoutSeconds = 30), y la versión 2 cambia el valor por defecto a 10. Una aplicación compilada contra la versión 1 sigue pasando 30 después de sustituir la DLL por la de la versión 2, hasta que la propia aplicación se recompila. Añadir un parámetro opcional nuevo a un método público existente también rompe a los llamadores ya compilados, porque la firma del método cambió y ellos siguen buscando la antigua (una MissingMethodException en tiempo de ejecución).

Dentro de una aplicación que se compila entera esto nunca importa. En las API públicas de paquetes NuGet, las sobrecargas (que mantienen los valores por defecto dentro de la biblioteca) o un valor por defecto null resuelto en el cuerpo evitan el problema.

Los valores por defecto y el tipo declarado

La misma regla de tiempo de compilación hace que, cuando una interfaz y una clase declaran valores por defecto, el valor venga del tipo de la variable a través de la que llamas, no del objeto:

Salida:

printing "report" x5
printing "report" x1

El mismo objeto, dos valores por defecto distintos. Mantén idénticos los valores por defecto entre una interfaz y sus implementaciones, o decláralos en un solo sitio.

Atributos de información del llamador

Los parámetros opcionales también hacen funcionar los atributos de información del llamador de System.Runtime.CompilerServices. El compilador los rellena con datos del punto de llamada:

Salida:

[Main:18] starting
[SaveOrder:13] order saved

Así es como las bibliotecas de logging registran de dónde vino un mensaje sin que quien llama lo escriba, y como las implementaciones de INotifyPropertyChanged obtienen el nombre de la propiedad. [CallerFilePath] añade la ruta del archivo fuente de la misma forma.

Preguntas frecuentes

¿Cómo hago que un parámetro sea opcional en C#?

Dale un valor por defecto en la declaración: static void Log(string message, string level = "INFO"). Así quien llama puede escribir Log("started") o Log("failed", "ERROR"). Los parámetros opcionales deben ir después de todos los obligatorios, y el valor por defecto debe ser una constante en tiempo de compilación.

¿Qué son los argumentos con nombre en C#?

Argumentos que se pasan con el nombre del parámetro: SendEmail(to: "ana@mail.com", urgent: true). Permiten saltarse parámetros opcionales intermedios, pasar los argumentos en cualquier orden y hacer legibles de un vistazo las llamadas con true/false literales o con números.

¿Por qué no puedo usar DateTime.Now como valor por defecto de un parámetro?

Los valores por defecto deben ser constantes en tiempo de compilación, y DateTime.Now se calcula en tiempo de ejecución, así que el compilador da CS1736, Default parameter value for 'at' must be a compile-time constant. Usa en su lugar un parámetro nullable: DateTime? at = null, y después DateTime time = at ?? DateTime.Now; en el cuerpo.

¿Debo usar parámetros opcionales o sobrecargas de métodos en C#?

Los parámetros opcionales son más sencillos cuando las variantes solo se diferencian en los valores por defecto. Las sobrecargas son mejores cuando las variantes necesitan lógica o tipos distintos, y en bibliotecas públicas, porque un valor por defecto se compila dentro de cada llamador y cambiarlo después no llega al código ya compilado.

¿Qué significa "Optional parameters must appear after all required parameters"?

Error CS1737: a un parámetro con valor por defecto le sigue uno sin él. Pon primero los parámetros obligatorios: (string to, bool urgent = false), no (bool urgent = false, string to). Después de un parámetro opcional solo pueden ir más parámetros opcionales o un array params.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR