Atualmente estou escrevendo algum código para UnconstrainedMelody que tem métodos genéricos para fazer com enums.
Agora, eu tenho uma classe estática com um monte de métodos que são unicamente destinadas a ser usado com "bandeiras" enums. Eu não posso adicionar isso como uma restrição ... então é possível que eles sejam chamados com outros tipos de enum também. Nesse caso, gostaria de lançar uma exceção, mas não tenho certeza de qual lançar.
Só para tornar isso concreto, se eu tiver algo assim:
// Returns a value with all bits set by any values
public static T GetBitMask<T>() where T : struct, IEnumConstraint
{
if (!IsFlags<T>()) // This method doesn't throw
{
throw new ???
}
// Normal work here
}
Qual é a melhor exceção para lançar? ArgumentException
parece lógico, mas é um argumento de tipo em vez de um argumento normal, o que poderia facilmente confundir as coisas. Devo apresentar minha própria TypeArgumentException
classe? Use InvalidOperationException
? NotSupportedException
? Algo mais?
Eu sim não criar meu próprio exceção para isso a menos que seja claramente a coisa certa a fazer.
Respostas:
NotSupportedException
parece que se encaixa perfeitamente, mas a documentação afirma claramente que deve ser usado para uma finalidade diferente. Dos comentários da classe MSDN:Claro, há uma maneira que
NotSupportedException
é obviamente boa o suficiente, especialmente devido ao seu significado de senso comum. Dito isso, não tenho certeza se está certo.Dado o propósito de Melodia Irrestrita ...
... parece que um novo
Exception
pode estar em ordem, apesar do alto ônus da prova que, com justiça, temos que enfrentar antes de criar o personalizadoExceptions
. Algo assimInvalidTypeParameterException
pode ser útil em toda a biblioteca (ou talvez não - este é certamente um caso extremo, certo?).Os clientes precisarão ser capazes de distinguir isso das exceções BCL? Quando um cliente pode acidentalmente chamar isso usando uma baunilha
enum
? Como você responderia às perguntas feitas pela resposta aceita para Quais fatores devem ser levados em consideração ao escrever uma classe de exceção personalizada?fonte
InvalidOperationException
são nojentas, porque "Foo pede a coleção Bar para adicionar algo que já existe, então Bar lança IOE" e "Foo pede coleção Bar para adicionar algo, então Bar chama Boz que lança IOE mesmo que Bar não esteja esperando" ambos irão lançar o mesmo tipo de exceção; o código que espera capturar o primeiro não estará esperando o último. Dito isto ...Foo<T>
a um "tipo geral" eFoo<Bar>
um "tipo específico" naquele contexto, embora não haja nenhuma relação de "herança" entre eles.Eu evitaria NotSupportedException. Esta exceção é usada na estrutura onde um método não é implementado e há uma propriedade indicando que este tipo de operação não é suportado. Não cabe aqui
Acho que InvalidOperationException é a exceção mais apropriada que você pode lançar aqui.
fonte
T
é umenum
decorado comFlags
, seria válido lançar o NSE.StupidClrException
faz um nome divertido;)A programação genérica não deve lançar em tempo de execução para parâmetros de tipo inválido. Ele não deve compilar, você deve ter uma aplicação de tempo de compilação. Não sei o que
IsFlag<T>()
contém, mas talvez você possa transformar isso em uma aplicação de tempo de compilação, como tentar criar um tipo que só é possível criar com 'sinalizadores'. Talvez umatraits
aula possa ajudar.Atualizar
Se você deve jogar, eu votaria em InvalidOperationException. O raciocínio é que os tipos genéricos têm parâmetros e os erros relacionados aos parâmetros (método) são centralizados na hierarquia ArgumentException. No entanto, a recomendação em ArgumentException afirma que
Há pelo menos um salto de fé nisso, que as recomendações de parâmetros de método também devem ser aplicadas a parâmetros genéricos , mas não há nada melhor no SystemException hierachy imho.
fonte
IsFlag<T>
determina se o enum foi[FlagsAttribute]
aplicado a ele e o CLR não tem restrições baseadas em atributos. Seria em um mundo perfeito - ou haveria alguma outra maneira de restringi-lo - mas neste caso simplesmente não funciona :(Eu usaria NotSupportedException, pois é isso que você está dizendo. Outros enums além dos específicos não são suportados . É claro que isso seria declarado mais claramente na mensagem de exceção.
fonte
Eu iria com
NotSupportedException
. EmboraArgumentException
pareça bom, é realmente esperado quando um argumento passado para um método é inaceitável. Um argumento de tipo é uma característica definidora do método real que você deseja chamar, não um "argumento" real.InvalidOperationException
deve ser acionado quando a operação que você está realizando pode ser válida em alguns casos, mas para a situação específica, é inaceitável.NotSupportedException
é lançado quando uma operação é inerentemente sem suporte. Por exemplo, ao implementar uma interface onde um membro específico não faz sentido para uma classe. Isso parece uma situação semelhante.fonte
Aparentemente, a Microsoft usa
ArgumentException
para isso, conforme demonstrado no exemplo de Expression.Lambda <> , Enum.ExperimenteParse <> ou Marshal.GetDelegateForFunctionPointer <> na seção Exceções. Não consegui encontrar nenhum exemplo indicando o contrário (apesar de pesquisar na fonte de referência localTDelegate
eTEnum
).Portanto, acho que é seguro presumir que, pelo menos no código da Microsoft, é uma prática comum usar
ArgumentException
para argumentos de tipo genérico inválidos, além dos de variáveis básicas. Dado que a descrição da exceção nos documentos não faz distinção entre elas, também não é muito forçado.Esperançosamente, ele decide as questões de uma vez por todas.
fonte
TypeArgumentException
deArgumentException
, simplesmente porque o tipo de argumento não é um regular argumento.Ide ir com NotSupportedExpcetion.
fonte
Lançar uma exceção feita sob encomenda deve sempre ser feito em qualquer caso em que seja questionável. Uma exceção personalizada sempre funcionará, independentemente das necessidades dos usuários da API. O desenvolvedor pode capturar qualquer tipo de exceção se ele não se importar, mas se o desenvolvedor precisar de tratamento especial, ele será SOL.
fonte
Que tal herdar de NotSupportedException. Embora eu concorde com @Mehrdad que faz mais sentido, ouço seu ponto de que não parece se encaixar perfeitamente. Portanto, herde de NotSupportedException e, dessa forma, as pessoas codificando em sua API ainda podem capturar uma NotSupportedException.
fonte
Sempre fico cauteloso ao escrever exceções personalizadas, simplesmente porque elas nem sempre são documentadas de forma clara e causam confusão se não forem nomeadas corretamente.
Nesse caso, eu lançaria uma ArgumentException para a falha de verificação de sinalizadores. É tudo uma questão de preferência. Alguns padrões de codificação que vi vão tão longe a ponto de definir quais tipos de exceções devem ser lançados em cenários como este.
Se o usuário estava tentando passar algo que não era um enum, eu lançaria um InvalidOperationException.
Editar:
Os outros levantam um ponto interessante de que isso não é suportado. Minha única preocupação com uma NotSupportedException é que geralmente essas são as exceções que são lançadas quando a "matéria escura" é introduzida no sistema ou, dito de outra forma, "Este método deve entrar no sistema nesta interface, mas nós vencemos não ligue até a versão 2.4 "
Eu também vi NotSupportedExceptions ser lançada como uma exceção de licenciamento "você está executando a versão gratuita deste software, esta função não é compatível".
Editar 2:
Outro possível:
A exceção lançada ao usar argumentos inválidos que são enumeradores.
fonte
LicensingException
classe que herda deInvalidOperationException
.Eu também votaria em InvalidOperationException. Fiz um fluxograma (incompleto) nas diretrizes de lançamento de exceções do .NET com base nas Diretrizes de Design do Framework 2ª ed. há algum tempo se alguém estiver interessado.
fonte