Ambos generan un error que dice que deben ser una constante en tiempo de compilación:
void Foo(TimeSpan span = TimeSpan.FromSeconds(2.0))
void Foo(TimeSpan span = new TimeSpan(2000))
En primer lugar, ¿alguien puede explicar por qué estos valores no se pueden determinar en tiempo de compilación? ¿Y hay alguna manera de especificar un valor predeterminado para un objeto TimeSpan opcional?
c#
c#-4.0
default-value
timespan
optional-parameters
Mike Pateras
fuente
fuente
new TimeSpan(2000)
no significa 2000 milisegundos, significa 2000 "ticks", que son 0.2 milisegundos, o una 10,000-th de dos segundos.Respuestas:
Puede solucionar este problema muy fácilmente cambiando su firma.
Debo explicar: la razón por la cual esas expresiones en su ejemplo no son constantes de tiempo de compilación es porque en el momento de la compilación, el compilador no puede simplemente ejecutar TimeSpan.FromSeconds (2.0) y pegar los bytes del resultado en su código compilado.
Como ejemplo, considere si intentó usar DateTime.Now en su lugar. El valor de DateTime.Now cambia cada vez que se ejecuta. O suponga que TimeSpan.FromSeconds tuvo en cuenta la gravedad. Es un ejemplo absurdo, pero las reglas de las constantes de tiempo de compilación no hacen casos especiales solo porque sabemos que TimeSpan.FromSeconds es determinista.
fuente
<param>
, porque no está visible en la firma.span = span ?? TimeSpan.FromSeconds(2.0);
con el tipo anulable, en el cuerpo del método. Ovar realSpan = span ?? TimeSpan.FromSeconds(2.0);
para obtener una variable local que no sea anulable.Mi herencia VB6 me inquieta con la idea de considerar que el "valor nulo" y el "valor perdido" son equivalentes. En la mayoría de los casos, probablemente esté bien, pero puede tener un efecto secundario no deseado, o puede tragarse una condición excepcional (por ejemplo, si la fuente
span
es una propiedad o variable que no debería ser nula, pero lo es).Por lo tanto, sobrecargaría el método:
fuente
Esto funciona bien:
void Foo(TimeSpan span = default(TimeSpan))
fuente
TimeSpan
valores arbitrarios , como el dado pornew TimeSpan(2000)
.El conjunto de valores que se pueden usar como valor predeterminado son los mismos que se pueden usar para un argumento de atributo. La razón es que los valores predeterminados están codificados en metadatos dentro de
DefaultParameterValueAttribute
.En cuanto a por qué no se puede determinar en tiempo de compilación. El conjunto de valores y expresiones sobre dichos valores permitidos en tiempo de compilación se enumera en la especificación oficial del lenguaje C # :
El tipo
TimeSpan
no cabe en ninguna de estas listas y, por lo tanto, no se puede usar como una constante.fuente
TimeSpan
puede caber el último en esta listadefault(TimeSpan)
es válido.proporcionado
default(TimeSpan)
no es un valor válido para la función.O
proporcionado
new TimeSpan()
no es un valor válido.O
Esto debería ser mejor teniendo en cuenta que las posibilidades de que el
null
valor sea un valor válido para la función son poco frecuentes.fuente
TimeSpan
es un caso especial paraDefaultValueAttribute
y se especifica usando cualquier cadena que se pueda analizar a través delTimeSpan.Parse
método.fuente
Mi sugerencia:
Por cierto,
TimeSpan.FromSeconds(2.0)
no es igualnew TimeSpan(2000)
: el constructor toma las garrapatas.fuente
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 predeterminadonull
para él. Por ejemplo:Para llamar, use esa sintaxis extraña para crear instancias y asignar propiedades en una sola expresión:
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.
fuente