Por que lançar int para valor de enum inválido NÃO lança exceção?

Se eu tenho um enum como assim:

enum Beer { Bud = 10, Stella = 20, Unknown } 

Por que não lançar uma exceção ao converter um int que está fora desses valores para um tipo de Beer ?

Por exemplo, o código a seguir não lança uma exceção, ele gera ’50’ no console:

 int i = 50; var b = (Beer) i; Console.WriteLine(b.ToString()); 

Eu acho isso estranho … alguém pode esclarecer?

Retirado de Confusão com análise de um Enum

Esta foi uma decisão da parte das pessoas que criaram o .NET. Um enum é apoiado por outro tipo de valor ( int , short , byte , etc) e, portanto, pode realmente ter qualquer valor que seja válido para esses tipos de valor.

Eu pessoalmente não sou fã da maneira como isso funciona, então fiz uma série de methods de utilidade:

 ///  /// Utility methods for enum values. This static type will fail to initialize /// (throwing a ) if /// you try to provide a value that is not an enum. ///  /// An enum type.  public static class EnumUtil where T : struct, IConvertible // Try to get as much of a static check as we can. { // The .NET framework doesn't provide a compile-checked // way to ensure that a type is an enum, so we have to check when the type // is statically invoked. static EnumUtil() { // Throw Exception on static initialization if the given type isn't an enum. Require.That(typeof (T).IsEnum, () => typeof(T).FullName + " is not an enum type."); } ///  /// In the .NET Framework, objects can be cast to enum values which are not /// defined for their type. This method provides a simple fail-fast check /// that the enum value is defined, and creates a cast at the same time. /// Cast the given value as the given enum type. /// Throw an exception if the value is not defined for the given enum type. ///  ///  ///  ///  /// If the given value is not a defined value of the enum type. ///  ///  public static T DefinedCast(object enumValue) { if (!System.Enum.IsDefined(typeof(T), enumValue)) throw new InvalidCastException(enumValue + " is not a defined value for enum type " + typeof (T).FullName); return (T) enumValue; } ///  /// ///  ///  ///  public static T Parse(string enumValue) { var parsedValue = (T)System.Enum.Parse(typeof (T), enumValue); //Require that the parsed value is defined Require.That(parsedValue.IsDefined(), () => new ArgumentException(string.Format("{0} is not a defined value for enum type {1}", enumValue, typeof(T).FullName))); return parsedValue; } public static bool IsDefined(T enumValue) { return System.Enum.IsDefined(typeof (T), enumValue); } } public static class EnumExtensions { public static bool IsDefined(this T enumValue) where T : struct, IConvertible { return EnumUtil.IsDefined(enumValue); } } 

Desta forma, posso dizer:

 if(!sEnum.IsDefined()) throw new Exception(...); 

… ou:

 EnumUtil.Parse(s); // throws an exception if s is not a defined value. 

Editar

Além da explicação dada acima, você tem que perceber que a versão .NET do Enum segue um padrão mais inspirado em C do que um inspirado em Java. Isso torna possível ter enums “Bit Flag” que podem usar padrões binários para determinar se um “sinalizador” específico está ativo em um valor de enum. Se você tivesse que definir todas as combinações possíveis de flags (por MondayAndTuesday , MondayAndWednesdayAndThursday , MondayAndWednesdayAndThursday ), elas seriam extremamente tediosas. Portanto, ter a capacidade de usar valores de enumeração indefinidos pode ser muito útil. Ele requer apenas um pouco de trabalho extra quando você quer um comportamento fail-fast em tipos de enum que não aproveitam esses tipos de truques.

Enums são frequentemente usados ​​como sinalizadores:

 [Flags] enum Permission { None = 0x00, Read = 0x01, Write = 0x02, } ... Permission p = Permission.Read | Permission.Write; 

O valor de p é o inteiro 3, que não é um valor do enum, mas claramente é um valor válido.

Eu pessoalmente preferiria ter visto uma solução diferente; Eu preferiria ter a capacidade de fazer tipos inteiros de “matriz de bits” e tipos “um conjunto de valores distintos” como dois resources de linguagem diferentes em vez de confundi-los em “enum”. Mas é isso que os designers originais de linguagem e framework criaram; Como resultado, temos que permitir que valores não declarados do enum sejam valores legais.

A resposta curta: Os designers de linguagem decidiram projetar a linguagem dessa maneira.

A resposta longa: Section 6.2.2: Explicit enumeration conversions de Section 6.2.2: Explicit enumeration conversions da especificação de linguagem C # diz:

Uma conversão de enumeração explícita entre dois tipos é processada tratando qualquer tipo de enumeração participante como o tipo subjacente desse tipo de enumeração e, em seguida, executando uma conversão numérica implícita ou explícita entre os tipos resultantes. Por exemplo, dado um E do tipo enum com um tipo subjacente de int, uma conversão de E para byte é processada como uma conversão numérica explícita (§6.2.1) de int para byte, e uma conversão de byte para E é processada como uma conversão numérica implícita (§6.1.2) de byte para int.

Basicamente, o enum é tratado como o tipo subjacente quando se trata de fazer uma operação de conversão. Por padrão, o tipo subjacente de um enum é Int32 , o que significa que a conversão é tratada exatamente como uma conversão para Int32 . Isso significa que qualquer valor int válido é permitido.

Eu suspeito que isso foi feito principalmente por razões de desempenho. Tornando o enum um tipo integral simples e permitindo qualquer conversão de tipo integral, o CLR não precisa fazer todas as verificações extras. Isso significa que o uso de um enum não tem nenhuma perda de desempenho quando comparado ao uso de um inteiro, o que, por sua vez, ajuda a incentivar seu uso.

Da documentação :

Uma variável do tipo Dias pode ser atribuída a qualquer valor no intervalo do tipo subjacente; os valores não estão limitados às constantes nomeadas.