Actualmente estoy escribiendo un código para UnconstrainedMelody que tiene métodos genéricos relacionados con enumeraciones.
Ahora, tengo una clase estática con un montón de métodos que solo deben usarse con enumeraciones "flags". No puedo agregar esto como una restricción ... por lo que es posible que también se llamen con otros tipos de enumeración. En ese caso, me gustaría lanzar una excepción, pero no estoy seguro de cuál lanzar.
Solo para hacer esto concreto, si tengo algo como esto:
// 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
}
¿Cuál es la mejor excepción para lanzar? ArgumentException
suena lógico, pero es un argumento de tipo en lugar de un argumento normal, que podría confundir fácilmente las cosas. ¿Debo presentar mi propia TypeArgumentException
clase? Uso InvalidOperationException
? NotSupportedException
? ¿Algo más?
Yo más bien no crear mi propia excepción para esto a menos que sea claramente lo que hay que hacer.
Respuestas:
NotSupportedException
Parece que encaja perfectamente, pero la documentación establece claramente que debe usarse para un propósito diferente. De los comentarios de la clase de MSDN:Por supuesto, hay una forma en la que
NotSupportedException
obviamente es suficientemente buena, especialmente dado su significado de sentido común. Habiendo dicho eso, no estoy seguro de si está bien.Dado el propósito de Melodía sin restricciones ...
... parece que una nueva
Exception
podría estar en orden a pesar de la gran carga de pruebas que tenemos que cumplir antes de crear una costumbreExceptions
. Algo comoInvalidTypeParameterException
podría ser útil en toda la biblioteca (o tal vez no; este es seguramente un caso marginal, ¿verdad?).¿Los clientes deberán poder distinguir esto de las excepciones BCL? ¿Cuándo podría un cliente llamar accidentalmente a esto usando una vainilla
enum
? ¿Cómo respondería las preguntas planteadas por la respuesta aceptada a Qué factores deben tenerse en cuenta al escribir una clase de excepción personalizada?fuente
InvalidOperationException
son asquerosas, porque "Foo le pide a Collection Bar que agregue algo que ya existe, entonces Bar lanza IOE" y "Foo le pide a Collection Bar que agregue algo, por lo que Bar llama a Boz, que lanza IOE aunque Bar no lo espera" ambos arrojarán el mismo tipo de excepción; el código que espera captar el primero no espera el segundo. Habiendo dicho eso ...Foo<T>
es un "tipo general" yFoo<Bar>
un "tipo específico" en ese contexto, aunque no hay una relación de "herencia" entre ellos.Evitaría NotSupportedException. Esta excepción se utiliza en el marco donde no se implementa un método y hay una propiedad que indica que este tipo de operación no es compatible. No encaja aqui
Creo que InvalidOperationException es la excepción más apropiada que podría lanzar aquí.
fuente
T
estáenum
decorado conFlags
, sería válido lanzar NSE.StupidClrException
hace un nombre divertido;)La programación genérica no debe lanzarse en tiempo de ejecución para parámetros de tipo no válidos. No debería compilarse, debería tener una aplicación de tiempo de compilación. No sé qué
IsFlag<T>()
contiene, pero tal vez pueda convertir esto en una aplicación de tiempo de compilación, como intentar crear un tipo que solo es posible crear con 'banderas'. Quizás unatraits
clase pueda ayudar.Actualizar
Si debe lanzar, votaría por InvalidOperationException. El razonamiento es que los tipos genéricos tienen parámetros y los errores relacionados con los parámetros (métodos) se centran en la jerarquía ArgumentException. Sin embargo, la recomendación sobre ArgumentException establece que
Hay al menos un acto de fe allí, que las recomendaciones de parámetros de método también se deben aplicar a parámetros genéricos , pero no hay nada mejor en la jerarquía de SystemException en mi humilde opinión.
fuente
IsFlag<T>
determina si la enumeración se le ha[FlagsAttribute]
aplicado y el CLR no tiene restricciones basadas en atributos. Sería en un mundo perfecto, o habría alguna otra forma de restringirlo, pero en este caso simplemente no funciona :(Usaría NotSupportedException ya que eso es lo que está diciendo. Otros enumeraciones que los específicos son no compatibles . Por supuesto, esto se indicará más claramente en el mensaje de excepción.
fuente
Yo iría con
NotSupportedException
. SiArgumentException
bien se ve bien, realmente se espera cuando un argumento pasado a un método es inaceptable. Un argumento de tipo es una característica definitoria del método real al que desea llamar, no un "argumento" real.InvalidOperationException
debe lanzarse cuando la operación que está realizando puede ser válida en algunos casos, pero para la situación particular, es inaceptable.NotSupportedException
se lanza cuando una operación no es inherentemente compatible. Por ejemplo, al implementar una interfaz donde un miembro en particular no tiene sentido para una clase. Esto parece una situación similar.fuente
Aparentemente, Microsoft usa
ArgumentException
para eso, como se demuestra en el ejemplo de Expression.Lambda <> , Enum.TryParse <> o Marshal.GetDelegateForFunctionPointer <> en la sección Excepciones. Tampoco pude encontrar ningún ejemplo que indique lo contrario (a pesar de buscar una fuente de referencia local paraTDelegate
yTEnum
).Por lo tanto, creo que es seguro asumir que al menos en el código de Microsoft es una práctica común usar
ArgumentException
para argumentos de tipo genérico no válidos además de los de variable básica. Dado que la descripción de la excepción en los documentos no discrimina entre ellos, tampoco es demasiado exagerado.Ojalá se decida la cuestión de una vez por todas.
fuente
TypeArgumentException
deArgumentException
, simplemente porque un argumento de tipo no es un habitual argumento.Iré con NotSupportedExpcetion.
fuente
Lanzar una excepción personalizada siempre debe realizarse en cualquier caso en el que sea cuestionable. Una excepción personalizada siempre funcionará, independientemente de las necesidades de los usuarios de la API. El desarrollador podría detectar cualquier tipo de excepción si no le importa, pero si el desarrollador necesita un manejo especial, será SOL.
fuente
¿Qué tal heredar de NotSupportedException? Si bien estoy de acuerdo con @Mehrdad en que tiene más sentido, escuché tu punto de que no parece encajar perfectamente. Por lo tanto, herede de NotSupportedException, y de esa manera las personas que codifican contra su API aún pueden detectar una NotSupportedException.
fuente
Siempre desconfío de escribir excepciones personalizadas, simplemente porque no siempre se documentan claramente y causan confusión si no se nombran correctamente.
En este caso, lanzaría una ArgumentException para la falla de verificación de banderas. En realidad, todo depende de las preferencias. Algunos estándares de codificación que he visto van tan lejos como para definir qué tipos de excepciones deben lanzarse en escenarios como este.
Si el usuario intentaba pasar algo que no era una enumeración, lanzaría una InvalidOperationException.
Editar:
Los demás plantean un punto interesante de que esto no es compatible. Mi única preocupación con una NotSupportedException es que generalmente esas son las excepciones que se lanzan cuando se ha introducido "materia oscura" en el sistema, o para decirlo de otra manera, "Este método debe ingresar al sistema en esta interfaz, pero ganamos no lo encienda hasta la versión 2.4 "
También he visto NotSupportedExceptions lanzarse como una excepción de licencia "está ejecutando la versión gratuita de este software, esta función no es compatible".
Edición 2:
Otro posible:
La excepción lanzada cuando se utilizan argumentos no válidos que son enumeradores.
fuente
LicensingException
clase heredadaInvalidOperationException
.También votaría por InvalidOperationException. Hice un diagrama de flujo (incompleto) sobre las pautas de lanzamiento de excepciones de .NET basado en las Pautas de diseño de Framework 2nd Ed. hace un tiempo si alguien está interesado.
fuente