Como posso usar como padrão um parâmetro para Guid.Empty em C #?

178

Eu gostaria de dizer:

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

Mas o compilador reclama que Guid.Empty não é uma constante de tempo de compilação.

Como não desejo alterar a API, não posso usar:

 Nullable<Guid>
Ian Ringrose
fonte
O que há de errado em mudar para Nullable<Guid> optional = null(ou mais rapidamente Guid? optional = null)? Qualquer Guid passado a ele será coagido sem nenhuma alteração de código necessária.
NH.

Respostas:

235

Solução

Você pode usar new Guid()em vez

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

Você também pode usar default(Guid)

default(Guid)também funcionará exatamente como new Guid().

Como Guid é um tipo de valor, não um tipo de referência, portanto, default(Guid)não é igual a, nullpor exemplo, em vez disso, é igual a chamar o construtor padrão.

O que significa que isso:

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

É exatamente o mesmo que o exemplo original.

Explicação

Por que não Guid.Emptyfuncionou?

O motivo pelo qual você está recebendo o erro é porque Emptyé definido como:

public static readonly Guid Empty;

Portanto, é uma variável, não uma constante (definida como static readonlynão como const). O compilador pode ter apenas valores conhecidos do compilador como valores padrão dos parâmetros do método (não somente conhecidos em tempo de execução).

A causa raiz é que você não pode ter constnenhum struct, ao contrário enumdo exemplo. Se você tentar, ele não será compilado.

A razão mais uma vez é que structnão é um tipo primitivo.
Para obter uma lista de todos os tipos primitivos no .NET, consulte http://msdn.microsoft.com/en-gb/library/system.typecode.aspx
(observe que enumgeralmente herda int, que é uma primitiva)

Mas new Guid()também não é uma constante!

Não estou dizendo que precisa de uma constante. Precisa de algo que possa ser decidido em tempo de compilação. Emptyé um campo, portanto, seu valor não é conhecido no tempo de compilação (apenas no início do tempo de execução).

O valor padrão do parâmetro deve ser conhecido em tempo de compilação, que pode ser um constvalor, ou algo definido usando um recurso C # que torna o valor conhecido em tempo de compilação, como default(Guid)or new Guid()(que é decidido em tempo de compilação por structs, pois você não pode modificar o structconstrutor em código).

Embora você possa fornecer defaultou newfacilmente, você não pode fornecer um const(porque não é um tipo primitivo ou enumcomo explicado acima). Portanto, novamente, sem dizer que o parâmetro opcional em si precisa de um valor constante, mas conhecido do compilador.

Meligy
fonte
5
Bem, ele não precisa de uma expressão constante normal - new Guid()não é uma expressão constante, por exemplo. A especificação C # define claramente o que é permitido, incluindo mas não limitado a constantes. (Só para ficar claro, ele efetivamente é uma constante de tempo de compilação, não apenas uma "expressão constante" em termos C # especificação).
Jon Skeet
3
Leia a parte Explicação na minha resposta. Adicionado para responder a esta parte.
Meligy
Agradável explicação.
Daryl
Você pode usar somente defaulthoje :)
Joshit
151

Guid.Emptyé equivalente a new Guid(), que é equivalente a default(Guid). Então você pode usar:

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

ou

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

Observe que o new Foo()valor é aplicável apenas quando:

  • Você está realmente chamando de sem parâmetros construtor sem
  • Foo é um tipo de valor

Em outras palavras, quando o compilador sabe que é realmente apenas o valor padrão para o tipo :)

(Curiosamente, tenho 99,9% de certeza de que ele não chamará nenhum new Foo()construtor personalizado que você possa ter criado. Você não pode criar esse construtor em um tipo de valor em C #, mas pode fazê-lo em IL.)

Você pode usar a default(Foo)opção para qualquer tipo.

Jon Skeet
fonte
Agora, por que a mensagem de erro do compilador não me diz isso? O complier pode verificar o caso de Guid.Empty e fornecer uma mensagem mais útil.
Ian Ringrose
4
@ Ian Ringrose: Eu não acho que o compilador deva ter mensagens específicas de tipo em geral, para ser honesto.
Jon tiro ao prato
2
Definir um parâmetro como padrão para um novo objeto cria um novo objeto toda vez que o método é chamado no PHP; mas cria apenas um objeto para todo o programa em Python. Realmente, considero esta uma das poucas falhas de design do Python . Estou um pouco feliz por o C # (e o VB.Net) ter evitado esse problema, simplesmente desautorizando novos objetos nos parâmetros padrão ... embora haja momentos em que essa capacidade é realmente agradável no PHP.
BlueRaja - Danny Pflughoeft
1
qual poderia ser a razão pela qual essa mesma coisa não funcionaria na ação do controlador ASP.NET MVC? Todos os outros parâmetros opcionais funcionam (int, string), mas não funcionam para GUID, diz "Erro do servidor no aplicativo '/'. O dicionário de parâmetros contém uma entrada nula para o parâmetro 'categoryId' do tipo não anulável 'System.Guid '.. "O código compila bem com as especificações (padrão (Guid) e com o novo Guid ()), mas emite esse erro.
mare
@mare: eu não sei, eu tenho medo. Outra opção seria usar Nullable<Guid>, potencialmente.
Jon Skeet
18

Você não pode usar:

default ( Guid ) ?

usuario
fonte
1
Não. Operator '??' cannot be applied to operands of type 'System.Guid' and 'System.Guid'
recursivo
4
Desculpe, eu não quis dizer ?? como operador, mas como um ponto de interrogação enfatizado - eu vou editar!
19413 Nick
9

A resposta aceita não funciona no ASP.NET MVC e causa este erro em tempo de execução:

[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)' ....

Em vez disso, você pode fazer o seguinte:

public void Problem(Guid? optional)
{
    if (optional == null)
    {
        optional = new Guid();
    }
}
Majix
fonte
Eu vejo a razão para baixo direito a voto: a questão especifica que API não pode ser alterado para usar Nullable <Guid> - bastante justo
Majix
A menos que você queira atribuir explicitamente o parâmetro "opcional" com um valor Guid vazio, acho que essa é a maneira mais natural de definir um parâmetro opcional do tipo Guid.
Gonzalo Méndez
4

O compilador está bastante correto; Guid.Emptynão é uma constante em tempo de compilação. Você pode tentar sobrecarregar um método como este:

public void Problem()
{
    Problem(Guid.Empty);
}
um CVn
fonte
Eu disse que não queria mudar a API, o método que estou tentando domar tem mais de 10 parms!
Ian Ringrose
@ Ian Ringrose, embora eu concorde com Guid x = default(Guid)a solução, lembre-se de que adicionar outra sobrecarga de função não complica a API mais do que adicionar um argumento opcional. Isso é realmente o que um argumento opcional faz de qualquer maneira.
tenfour
@ tenfour, teria que haver muitas sobrecargas de funções novas para fazer o mesmo que 10 parms opcionais!
Ian Ringrose
Se você tem uma função pública que requer mais de dez parâmetros, talvez tornar opcional um único argumento não seja realmente uma correção ... Além disso, olhando para a pergunta original, você realmente diz que não deseja "alterar o API "(e, como aponta dez, a diferença entre uma sobrecarga explícita e um argumento opcional é mínima na prática), mas não há menção na questão da lista de parâmetros ser esse tipo de monstro.
a CVn 28/02
Na verdade, esta é uma resposta perfeita para o problema.
PKD