162 votes

Pourquoi la conversion de int en valeur invalide ne génère-t-elle pas une exception?

Si j'ai un enum comme ça:

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

Pourquoi ne jette-t-il pas une exception lors de la conversion d'un int dehors de ces valeurs vers un type de Beer ?

Par exemple, le code suivant ne lève pas d'exception, il écrit '50' sur la console:

 int i = 50;
var b = (Beer) i;

Console.WriteLine(b.ToString());
 

Je trouve cela étrange ... quelqu'un peut-il préciser?

96voto

StriplingWarrior Points 56276

Pris de Confusion avec l'analyse d'un Enum

C'était une décision de la part de ceux qui les ont créés .NET. Un enum est soutenu par un autre type de valeur (int, short, byte, etc), et donc cela peut avoir n'importe quelle valeur qui est valable pour les types de valeur.

Personnellement, je ne suis pas un fan de la façon dont cela fonctionne, j'ai donc fait une série de méthodes de service:

/// <summary>
/// Utility methods for enum values. This static type will fail to initialize 
/// (throwing a <see cref="TypeInitializationException"/>) if
/// you try to provide a value that is not an enum.
/// </summary>
/// <typeparam name="T">An enum type. </typeparam>
public static class EnumUtil<T>
    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.");
    }

    /// <summary>
    /// 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.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="enumValue"></param>
    /// <exception cref="InvalidCastException">
    /// If the given value is not a defined value of the enum type.
    /// </exception>
    /// <returns></returns>
    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;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="enumValue"></param>
    /// <returns></returns>
    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);
    }

}

De cette façon, je peux dire:

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

... ou:

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

Modifier

Au-delà de l'explication donnée ci-dessus, vous vous rendez compte que l' .La version NET de Enum suit un plus C-modèle inspiré de Java est inspiré. Cela permet d'avoir de "Petit Pavillon" les énumérations qui peut utiliser des schémas binaires, afin de déterminer si un "drapeau" est active à une valeur d'enum. Si vous deviez définir toutes les combinaisons possibles de drapeaux (c - MondayAndTuesday, MondayAndWednesdayAndThursday), celle-ci serait extrêmement fastidieux. Donc, avoir la capacité de utiliser pas défini les valeurs de l'enum peut être vraiment pratique. Il faut juste un peu de travail supplémentaire si vous voulez un fail-fast comportement sur les types enum que de ne pas tirer parti de ces sortes de trucs.

66voto

Eric Lippert Points 300275

Les énumérations sont souvent utilisés comme indicateurs:

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

Permission p = Permission.Read | Permission.Write;

La valeur de p est le nombre entier 3, ce qui n'est pas une valeur de l'enum, mais clairement, c'est une valeur valide.

Personnellement, je préfère avoir vu une autre solution; j'aurais plutôt eu la possibilité de faire des "bits de tableau" types d'entiers et un ensemble de valeur distincte" des types comme deux différentes fonctionnalités de la langue plutôt que de juxtaposer les deux hommes dans "enum". Mais qu'est ce que la langue originale et le cadre designers ont créé avec, comme résultat, nous devons permettre à la non-déclaration de valeurs de l'énumération légale des valeurs.

21voto

Reed Copsey Points 315315

La réponse courte: La langue de créateurs ont décidé de concevoir la langue de cette façon.

La réponse longue: Section 6.2.2: Explicit enumeration conversions de la Spécification du Langage C# dit:

Une énumération explicite de conversion entre les deux types sont traitées par le traitement de tous les enum type que le type sous-jacent de cette enum type, et en effectuant ensuite, implicite ou explicite numérique la conversion entre les types qui en résultent. Par exemple, étant donné une énumération de type E et de type sous-jacent de type int, une conversion de l'E à l'octet est traitée comme une explicite conversion numérique (§6.2.1) à partir de int en octets, et une conversion à partir de l'octet de E est traitée comme un accord implicite de conversion numérique (§6.1.2) à partir de l'octet de type int.

Fondamentalement, l' enum est considérée comme le type sous-jacent lorsqu'il s'agit de faire une opération de conversion. Par défaut, un enum's sous-jacent est de type Int32, ce qui signifie que la conversion est traitée comme une conversion en Int32. Cela signifie que pour être valable, toute int de la valeur est admissible.

Je soupçonne que cela a été fait principalement pour des raisons de performances. En rendant enum un simple type intégral et en permettant à n'importe quel type intégral de conversion, le CLR n'a pas besoin de faire tous les contrôles supplémentaires. Cela signifie que l'utilisation d'un enum n'a pas vraiment de perte de performance lorsque comparé à l'utilisation d'un nombre entier, ce qui contribue à encourager son utilisation.

10voto

Martin Points 15155

De la documentation :

Une variable de type Jours peut recevoir toute valeur comprise dans la plage du type sous-jacent; les valeurs ne sont pas limitées aux constantes nommées.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X