Otras respuestas han dado excelentes explicaciones de por qué un parámetro opcional no puede ser una expresión dinámica. Pero, para contar, los parámetros predeterminados se comportan como constantes de tiempo de compilación. Eso significa que el compilador debe poder evaluarlos y encontrar una respuesta. Hay algunas personas que desean que C # agregue soporte para el compilador que evalúa expresiones dinámicas cuando se encuentra con declaraciones constantes; este tipo de característica estaría relacionada con los métodos de marcado "puros", pero eso no es una realidad en este momento y puede que nunca lo sea.
Una alternativa al uso de un parámetro predeterminado de C # para tal método sería usar el patrón ejemplificado por XmlReaderSettings
. En este patrón, defina una clase con un constructor sin parámetros y propiedades de escritura pública. Luego, reemplace todas las opciones por defecto en su método con un objeto de este tipo. Incluso haga que este objeto sea opcional especificando un valor predeterminado null
para él. Por ejemplo:
public class FooSettings
{
public TimeSpan Span { get; set; } = TimeSpan.FromSeconds(2);
// I imagine that if you had a heavyweight default
// thing you’d want to avoid instantiating it right away
// because the caller might override that parameter. So, be
// lazy! (Or just directly store a factory lambda with Func<IThing>).
Lazy<IThing> thing = new Lazy<IThing>(() => new FatThing());
public IThing Thing
{
get { return thing.Value; }
set { thing = new Lazy<IThing>(() => value); }
}
// Another cool thing about this pattern is that you can
// add additional optional parameters in the future without
// even breaking ABI.
//bool FutureThing { get; set; } = true;
// You can even run very complicated code to populate properties
// if you cannot use a property initialization expression.
//public FooSettings() { }
}
public class Bar
{
public void Foo(FooSettings settings = null)
{
// Allow the caller to use *all* the defaults easily.
settings = settings ?? new FooSettings();
Console.WriteLine(settings.Span);
}
}
Para llamar, use esa sintaxis extraña para crear instancias y asignar propiedades en una sola expresión:
bar.Foo(); // 00:00:02
bar.Foo(new FooSettings { Span = TimeSpan.FromDays(1), }); // 1.00:00:00
bar.Foo(new FooSettings { Thing = new MyCustomThing(), }); // 00:00:02
Desventajas
Este es un enfoque realmente pesado para resolver este problema. Si está escribiendo una interfaz interna rápida y sucia y hace que la TimeSpan
anulación y el tratamiento nulo como su valor predeterminado deseado funcionen bien, hágalo en su lugar.
Además, si tiene una gran cantidad de parámetros o está llamando al método en un ciclo cerrado, esto tendrá la sobrecarga de las instancias de clase. Por supuesto, si se llama a un método de este tipo en un ciclo cerrado, puede ser natural e incluso muy fácil reutilizar una instancia del FooSettings
objeto.
Beneficios
Como mencioné en el comentario del ejemplo, creo que este patrón es excelente para las API públicas. Agregar nuevas propiedades a una clase es un cambio ABI ininterrumpido, por lo que puede agregar nuevos parámetros opcionales sin cambiar la firma de su método utilizando este patrón, lo que brinda más opciones al código compilado más recientemente mientras continúa admitiendo el código compilado anterior sin trabajo adicional .
Además, debido a que los parámetros de método predeterminados integrados de C # se tratan como constantes de tiempo de compilación y se incorporan al sitio de llamadas, los parámetros predeterminados solo serán utilizados por el código una vez que se vuelva a compilar. Al crear una instancia de un objeto de configuración, la persona que llama carga dinámicamente los valores predeterminados cuando llama a su método. Esto significa que puede actualizar los valores predeterminados simplemente cambiando su clase de configuración. Por lo tanto, este patrón le permite cambiar los valores predeterminados sin tener que volver a compilar las personas que llaman para ver los nuevos valores, si así lo desea.
new TimeSpan(2000)
no significa 2000 milisegundos, significa 2000 "ticks", que son 0.2 milisegundos, o una 10,000-th de dos segundos.