¿Cómo puedo predeterminar un parámetro a Guid.Empty en C #?

178

Deseo decir:

public void Problem(Guid optional = Guid.Empty)
{
}

Pero el compilador se queja de que Guid.Empty no es una constante de tiempo de compilación.

Como no deseo cambiar la API, no puedo usar:

 Nullable<Guid>
Ian Ringrose
fuente
¿Qué hay de malo en cambiar a Nullable<Guid> optional = null(o más brevemente Guid? optional = null)? Cualquier guía que se le pase actualmente coaccionará sin ningún cambio de código necesario.
NH.

Respuestas:

235

Solución

Puedes usar new Guid()en su lugar

public void Problem(Guid optional = new Guid())
{
  // when called without parameters this will be true
  var guidIsEmpty = optional == Guid.Empty;
}

También puedes usar default(Guid)

default(Guid)También funcionará exactamente como new Guid().

Debido a que Guid es un tipo de valor, no un tipo de referencia, por lo tanto, default(Guid)no es igual a, nullpor ejemplo, es igual a llamar al constructor predeterminado.

Lo que significa que esto:

public void Problem(Guid optional = default(Guid))
{
  // when called without parameters this will be true
  var guidIsEmpty = optional == Guid.Empty;
}

Es exactamente lo mismo que el ejemplo original.

Explicación

¿Por qué no Guid.Emptyfuncionó?

La razón por la que obtiene el error es porque Emptyse define como:

public static readonly Guid Empty;

Entonces, es una variable, no una constante (definida como static readonlyno como const). El compilador solo puede tener valores conocidos del compilador como valores predeterminados de los parámetros del método (no conocidos solo en tiempo de ejecución).

La causa raíz es que no puedes tener un const ningunastruct , a diferencia de, enumpor ejemplo. Si lo prueba, no se compilará.

La razón una vez más es que structno es un tipo primitivo.
Para obtener una lista de todos los tipos primitivos en .NET, consulte http://msdn.microsoft.com/en-gb/library/system.typecode.aspx
(tenga en cuenta queenum generalmente heredaint , que es un primitivo)

Pero new Guid() tampoco es una constante!

No digo que necesite una constante. Necesita algo que se pueda decidir en tiempo de compilación.Emptyes un campo, por lo tanto, su valor no se conoce en tiempo de compilación (solo al comienzo del tiempo de ejecución).

El valor predeterminado del parámetro debe conocerse en tiempo de compilación, que puede ser un constvalor o algo definido mediante una función de C # que hace que el valor sea conocido en tiempo de compilación, como default(Guid)o new Guid()(que se decide en tiempo de compilación parastruct s ya que no puede modificar el structconstructor en código).

Si bien puede proporcionar defaulto no new, no puede proporcionar un const(porque no es un tipo primitivo o enumcomo se explicó anteriormente). Entonces, nuevamente, no digo que el parámetro opcional en sí mismo necesite un valor conocido constante, pero compilador.

Meligy
fuente
55
Bueno, no necesita una expresión constante normal, new Guid()no es una expresión constante, por ejemplo. La especificación de C # define claramente lo que está permitido, incluidas , entre otras, las constantes. (Solo para ser claros, efectivamente es una constante de tiempo de compilación, solo que no es una "expresión constante" en términos específicos de C #).
Jon Skeet
3
Lea la parte Explicación en mi respuesta. Añadido para responder esta parte.
Meligy
Buena explicación ¡Gracias!
Daryl
Solo se puede usar defaulthoy en día :)
Joshit
151

Guid.Emptyes equivalente a new Guid(), que es equivalente a default(Guid). Entonces puedes usar:

public void Problem(Guid optional = default(Guid))

o

public void Problem(Guid optional = new Guid())

Tenga en cuenta que el new Foo()valor solo es aplicable cuando:

  • Realmente estás llamando a lo sin parámetros constructor sin
  • Foo es un tipo de valor

En otras palabras, cuando el compilador sabe que realmente es solo el valor predeterminado para el tipo :)

(Curiosamente, estoy 99.9% seguro de que no llamará a ningún new Foo()constructor personalizado que haya creado. No puede crear dicho constructor en un tipo de valor en C #, pero puede hacerlo en IL).

Puede usar la default(Foo)opción para cualquier tipo.

Jon Skeet
fuente
Ahora, ¿por qué el mensaje de error del compilador no me dice esto? El compilador podría verificar el caso de Guid.Empty y dar un mensaje más útil.
Ian Ringrose
44
@ Ian Ringrose: No creo que el compilador deba tener mensajes específicos de tipo en general, para ser honesto.
Jon Skeet
2
Establecer un parámetro como predeterminado para un nuevo objeto crea un nuevo objeto cada vez que se llama al método en PHP; pero solo crea un objeto para todo el programa en Python. Realmente, considero que este es uno de los pocos defectos de diseño de Python . Estoy un tanto contento de que C # (y VB.Net) hayan evitado este problema simplemente rechazando nuevos objetos en los parámetros predeterminados ... aunque hay momentos en los que esta capacidad es realmente buena en PHP.
BlueRaja - Danny Pflughoeft
1
¿Cuál podría ser la razón por la que esto mismo no funcionaría en la acción del controlador ASP.NET MVC? Todos los demás parámetros opcionales funcionan (int, string) pero no funcionan para GUID, dice "Error del servidor en la aplicación '/'. El diccionario de parámetros contiene una entrada nula para el parámetro 'categoryId' de tipo no anulable 'System.Guid '.. "El código se compila bien con ambas especificaciones (predeterminado (Guid) y con el nuevo Guid ()) pero emite este error.
yegua
@mare: No sé, me temo. Otra opción sería usar Nullable<Guid>, potencialmente.
Jon Skeet
18

¿No puedes usar:

default ( Guid ) ?

Mella
fuente
1
No. Operator '??' cannot be applied to operands of type 'System.Guid' and 'System.Guid'
recursivo el
44
Lo siento, no quise decir? como operador pero como un signo de interrogación enfatizado: ¡editaré!
Nick
9

La respuesta aceptada no funciona en ASP.NET MVC y causa este error de tiempo de ejecución:

[ArgumentException: The parameters dictionary contains a null entry for parameter 'optional' of non-nullable type 'System.Guid' for method 'System.Web.Mvc.ActionResult Problem(System.Guid)' ....

En su lugar, puede hacer lo siguiente:

public void Problem(Guid? optional)
{
    if (optional == null)
    {
        optional = new Guid();
    }
}
Majix
fuente
Veo el motivo de la votación negativa: la pregunta especifica que la API no se puede cambiar para usar Nullable <Guid> - bastante justo
Majix
A menos que desee asignar explícitamente el parámetro "opcional" con un valor Guid vacío, creo que esta es la forma más natural para definir un parámetro opcional de tipo Guid.
Gonzalo Méndez
4

El compilador es bastante correcto; Guid.EmptyNo es una constante de tiempo de compilación. Puede intentar hacer una sobrecarga de método como este:

public void Problem()
{
    Problem(Guid.Empty);
}
un CVn
fuente
Dije que no deseaba cambiar la API, ¡el método que estoy tratando de dominar tiene más de 10 parms!
Ian Ringrose
@Ian Ringrose, aunque estoy de acuerdo con Guid x = default(Guid)la solución, recuerde que agregar otra sobrecarga de funciones no complica más la API que agregar un argumento opcional. Eso es realmente lo que hace un argumento opcional de todos modos.
diez
@tenfour, ¡tendrían que haber muchas sobrecargas de funciones nuevas para hacer lo mismo que 10 parms opcionales!
Ian Ringrose
Si tiene una función pública que requiere más de diez parámetros, tal vez hacer que un solo argumento sea opcional no es realmente una solución ... Además, volviendo a la pregunta original, de hecho dice que no quiere "cambiar el API "(y como señala diez y cuatro, la diferencia entre una sobrecarga explícita y un argumento opcional es mínima en la práctica), pero no se menciona en la pregunta si la lista de parámetros es ese tipo de monstruo.
un CVn
En realidad, esta es una respuesta perfecta al problema.
PKD