Peut-on définir des conversions implicites d'énumérations en c #?

129

Est-il possible de définir une conversion implicite d'énumérations en c #?

quelque chose qui pourrait y parvenir?

public enum MyEnum
{
    one = 1, two = 2
}

MyEnum number = MyEnum.one;
long i = number;

Sinon, pourquoi pas?

Adam Naylor
la source
2
J'aimerais faire ça aussi. Nous avons une énumération enum YesNo {Yes, No}qui pourrait implicitement se convertir en bool.
Colonel Panic
Notant que ce concept désactive la vérification de sécurité de type compilateur. À plus long terme, un raccourci de conversion explicite comme un '~' final pourrait être préférable.
crokusek
Le lien n'est plus valide - pouvons-nous soit supprimer le lien, soit republier le site Web quelque part?
ワ イ き ん ぐ

Réponses:

128

Il existe une solution. Considérer ce qui suit:

public sealed class AccountStatus
{
    public static readonly AccountStatus Open = new AccountStatus(1);
    public static readonly AccountStatus Closed = new AccountStatus(2);

    public static readonly SortedList<byte, AccountStatus> Values = new SortedList<byte, AccountStatus>();
    private readonly byte Value;

    private AccountStatus(byte value)
    {
        this.Value = value;
        Values.Add(value, this);
    }


    public static implicit operator AccountStatus(byte value)
    {
        return Values[value];
    }

    public static implicit operator byte(AccountStatus value)
    {
        return value.Value;
    }
}

Ce qui précède offre une conversion implicite:

        AccountStatus openedAccount = 1;            // Works
        byte openedValue = AccountStatus.Open;      // Works

C'est un peu plus de travail que de déclarer une énumération normale (bien que vous puissiez refactoriser certains des éléments ci-dessus dans une classe de base générique commune). Vous pouvez aller encore plus loin en demandant à la classe de base d'implémenter IComparable & IEquatable, ainsi qu'en ajoutant des méthodes pour renvoyer la valeur de DescriptionAttributes, les noms déclarés, etc., etc.

J'ai écrit une classe de base (RichEnum <>) pour gérer la plupart des travaux difficiles, ce qui facilite la déclaration d'énumérations ci-dessus jusqu'à:

public sealed class AccountStatus : RichEnum<byte, AccountStatus>
{
    public static readonly AccountStatus Open = new AccountStatus(1);
    public static readonly AccountStatus Closed = new AccountStatus(2);

    private AccountStatus(byte value) : base (value)
    {
    }

    public static implicit operator AccountStatus(byte value)
    {
        return Convert(value);
    }
}

La classe de base (RichEnum) est répertoriée ci-dessous.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Resources;

namespace Ethica
{
    using Reflection;
    using Text;

    [DebuggerDisplay("{Value} ({Name})")]
    public abstract class RichEnum<TValue, TDerived>
                : IEquatable<TDerived>,
                  IComparable<TDerived>,
                  IComparable, IComparer<TDerived>
        where TValue : struct , IComparable<TValue>, IEquatable<TValue>
        where TDerived : RichEnum<TValue, TDerived>
    {
        #region Backing Fields

        /// <summary>
        /// The value of the enum item
        /// </summary>
        public readonly TValue Value;

        /// <summary>
        /// The public field name, determined from reflection
        /// </summary>
        private string _name;

        /// <summary>
        /// The DescriptionAttribute, if any, linked to the declaring field
        /// </summary>
        private DescriptionAttribute _descriptionAttribute;

        /// <summary>
        /// Reverse lookup to convert values back to local instances
        /// </summary>
        private static SortedList<TValue, TDerived> _values;

        private static bool _isInitialized;


        #endregion

        #region Constructors

        protected RichEnum(TValue value)
        {
            if (_values == null)
                _values = new SortedList<TValue, TDerived>();
            this.Value = value;
            _values.Add(value, (TDerived)this);
        }

        #endregion

        #region Properties

        public string Name
        {
            get
            {
                CheckInitialized();
                return _name;
            }
        }

        public string Description
        {
            get
            {
                CheckInitialized();

                if (_descriptionAttribute != null)
                    return _descriptionAttribute.Description;

                return _name;
            }
        }

        #endregion

        #region Initialization

        private static void CheckInitialized()
        {
            if (!_isInitialized)
            {
                ResourceManager _resources = new ResourceManager(typeof(TDerived).Name, typeof(TDerived).Assembly);

                var fields = typeof(TDerived)
                                .GetFields(BindingFlags.Static | BindingFlags.GetField | BindingFlags.Public)
                                .Where(t => t.FieldType == typeof(TDerived));

                foreach (var field in fields)
                {

                    TDerived instance = (TDerived)field.GetValue(null);
                    instance._name = field.Name;
                    instance._descriptionAttribute = field.GetAttribute<DescriptionAttribute>();

                    var displayName = field.Name.ToPhrase();
                }
                _isInitialized = true;
            }
        }

        #endregion

        #region Conversion and Equality

        public static TDerived Convert(TValue value)
        {
            return _values[value];
        }

        public static bool TryConvert(TValue value, out TDerived result)
        {
            return _values.TryGetValue(value, out result);
        }

        public static implicit operator TValue(RichEnum<TValue, TDerived> value)
        {
            return value.Value;
        }

        public static implicit operator RichEnum<TValue, TDerived>(TValue value)
        {
            return _values[value];
        }

        public static implicit operator TDerived(RichEnum<TValue, TDerived> value)
        {
            return value;
        }

        public override string ToString()
        {
            return _name;
        }

        #endregion

        #region IEquatable<TDerived> Members

        public override bool Equals(object obj)
        {
            if (obj != null)
            {
                if (obj is TValue)
                    return Value.Equals((TValue)obj);

                if (obj is TDerived)
                    return Value.Equals(((TDerived)obj).Value);
            }
            return false;
        }

        bool IEquatable<TDerived>.Equals(TDerived other)
        {
            return Value.Equals(other.Value);
        }


        public override int GetHashCode()
        {
            return Value.GetHashCode();
        }

        #endregion

        #region IComparable Members

        int IComparable<TDerived>.CompareTo(TDerived other)
        {
            return Value.CompareTo(other.Value);
        }

        int IComparable.CompareTo(object obj)
        {
            if (obj != null)
            {
                if (obj is TValue)
                    return Value.CompareTo((TValue)obj);

                if (obj is TDerived)
                    return Value.CompareTo(((TDerived)obj).Value);
            }
            return -1;
        }

        int IComparer<TDerived>.Compare(TDerived x, TDerived y)
        {
            return (x == null) ? -1 :
                   (y == null) ? 1 :
                    x.Value.CompareTo(y.Value);
        }

        #endregion

        public static IEnumerable<TDerived> Values
        {
            get
            {
                return _values.Values;
            }
        }

        public static TDerived Parse(string name)
        {
            foreach (TDerived value in _values.Values)
                if (0 == string.Compare(value.Name, name, true) || 0 == string.Compare(value.DisplayName, name, true))
                    return value;

            return null;
        }
    }
}
marque
la source
Correction d'une petite erreur décalée dans le message :-) C'est l'opérateur implicite statique public AccountStatus (valeur d'octet) {return Convert (valeur); } NOT return Convert (octet);
Mehdi LAMRANI
J'ai fait compiler cette classe de base. Cela vous dérange-t-il si je modifie les modifications?
sehe
64
Cette solution peut être «juste» comme exercice ou pour tester les compétences en programmation de quelqu'un, mais, s'il vous plaît, ne le faites pas dans la vraie vie. Non seulement c'est exagéré, c'est improductif, impossible à entretenir et moche comme l'enfer. Vous n'avez pas besoin d'utiliser une énumération juste pour le plaisir. Soit vous mettez un cast explicite, soit vous écrivez simplement une classe statique avec const ints.
Trap
3
N'est-ce pas fondamentalement une énumération Java réimplémentée?
Agent_L
2
Un problème majeur est que vous ne pouvez pas utiliser ces constantes statiques en lecture seule dans les instructions switch.
Ian Gold du
34

Vous ne pouvez pas faire de conversions implicites (sauf zéro), et vous ne pouvez pas écrire vos propres méthodes d'instance - cependant, vous pouvez probablement écrire vos propres méthodes d'extension:

public enum MyEnum { A, B, C }
public static class MyEnumExt
{
    public static int Value(this MyEnum foo) { return (int)foo; }
    static void Main()
    {
        MyEnum val = MyEnum.A;
        int i = val.Value();
    }
}

Cela ne vous donne pas beaucoup, cependant (par rapport à simplement faire un casting explicite).

L'une des principales fois où j'ai vu des gens le vouloir, c'est pour faire de la [Flags]manipulation via des génériques - c'est-à-dire une bool IsFlagSet<T>(T value, T flag);méthode. Malheureusement, C # 3.0 ne prend pas en charge les opérateurs sur les génériques, mais vous pouvez contourner ce problème en utilisant des choses comme celle-ci , qui rendent les opérateurs entièrement disponibles avec les génériques.

Marc Gravell
la source
Oui, c'était l'un de mes plus recherchés pour C # 4: stackoverflow.com/questions/138367/… et stackoverflow.com/questions/7244
Keith
@Keith - bon travail, il l'a fait, alors ;-p Le support dynamique / opérateur n'a pas été intégré au CTP, mais j'ai un banc d'essai prêt à l'emploi pour comparer les deux approches pour les opérateurs avec dynamique ( vs génériques / Expression) quand il y arrive.
Marc Gravell
@Keith - vous voudrez peut-être donner un tourbillon à la classe Operator de MiscUtil; Je suis presque sûr qu'il fera la plupart de ce que vous voulez.
Marc Gravell
22
struct PseudoEnum
{
    public const int 
              INPT = 0,
              CTXT = 1,
              OUTP = 2;
};

// ...

var arr = new String[3];

arr[PseudoEnum.CTXT] = "can";
arr[PseudoEnum.INPT] = "use";
arr[PseudoEnum.CTXT] = "as";
arr[PseudoEnum.CTXT] = "array";
arr[PseudoEnum.OUTP] = "index";
Glenn Slayden
la source
mais pourquoi struct?
Konrad
1
Aucune raison vraiment. Vous pourriez utiliser static classje suppose. Il n'y a aucun avantage à défendre l'un ou l'autre cas dans le ILcode final .
Glenn Slayden
18

J'ai adapté l'excellente classe de base générique RichEnum de Mark.

Fixation

  1. un certain nombre de problèmes de compilation dus à des bits manquants dans ses bibliothèques (notamment: les noms d'affichage dépendants des ressources n'ont pas été complètement supprimés; ils le sont maintenant)
  2. l'initialisation n'était pas parfaite: si la première chose que vous faisiez était d'accéder à la propriété statique .Values ​​depuis la classe de base, vous obtiendrez un NPE. Correction de ce problème en forçant la classe de base à être curieusement récursive ( CRTP ) la construction statique de TDerived juste à temps pendant CheckInitialized
  3. finalement déplacé la logique CheckInitialized dans un constructeur statique (pour éviter la pénalité de vérification à chaque fois, la condition de concurrence sur l'initialisation multithread; peut-être était-ce une impossibilité résolue par ma puce 1.?)

Félicitations à Mark pour la splendide idée + mise en œuvre, voici à vous tous:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Resources;

namespace NMatrix
{

    [DebuggerDisplay("{Value} ({Name})")]
    public abstract class RichEnum<TValue, TDerived>
                : IEquatable<TDerived>,
                  IComparable<TDerived>,
                  IComparable, IComparer<TDerived>
        where TValue : struct, IComparable<TValue>, IEquatable<TValue>
        where TDerived : RichEnum<TValue, TDerived>
    {
        #region Backing Fields

        /// <summary>
        /// The value of the enum item
        /// </summary>
        public readonly TValue Value;

        /// <summary>
        /// The public field name, determined from reflection
        /// </summary>
        private string _name;

        /// <summary>
        /// The DescriptionAttribute, if any, linked to the declaring field
        /// </summary>
        private DescriptionAttribute _descriptionAttribute;

        /// <summary>
        /// Reverse lookup to convert values back to local instances
        /// </summary>
        private static readonly SortedList<TValue, TDerived> _values = new SortedList<TValue, TDerived>();

        #endregion

        #region Constructors

        protected RichEnum(TValue value)
        {
            this.Value = value;
            _values.Add(value, (TDerived)this);
        }

        #endregion

        #region Properties

        public string Name
        {
            get
            {
                return _name;
            }
        }

        public string Description
        {
            get
            {
                if (_descriptionAttribute != null)
                    return _descriptionAttribute.Description;

                return _name;
            }
        }

        #endregion

        #region Initialization

        static RichEnum()
        {
            var fields = typeof(TDerived)
                .GetFields(BindingFlags.Static | BindingFlags.GetField | BindingFlags.Public)
                .Where(t => t.FieldType == typeof(TDerived));

            foreach (var field in fields)
            {
                /*var dummy =*/ field.GetValue(null); // forces static initializer to run for TDerived

                TDerived instance = (TDerived)field.GetValue(null);
                instance._name = field.Name;
                                    instance._descriptionAttribute = field.GetCustomAttributes(true).OfType<DescriptionAttribute>().FirstOrDefault();
            }
        }

        #endregion

        #region Conversion and Equality

        public static TDerived Convert(TValue value)
        {
            return _values[value];
        }

        public static bool TryConvert(TValue value, out TDerived result)
        {
            return _values.TryGetValue(value, out result);
        }

        public static implicit operator TValue(RichEnum<TValue, TDerived> value)
        {
            return value.Value;
        }

        public static implicit operator RichEnum<TValue, TDerived>(TValue value)
        {
            return _values[value];
        }

        public static implicit operator TDerived(RichEnum<TValue, TDerived> value)
        {
            return value;
        }

        public override string ToString()
        {
            return _name;
        }

        #endregion

        #region IEquatable<TDerived> Members

        public override bool Equals(object obj)
        {
            if (obj != null)
            {
                if (obj is TValue)
                    return Value.Equals((TValue)obj);

                if (obj is TDerived)
                    return Value.Equals(((TDerived)obj).Value);
            }
            return false;
        }

        bool IEquatable<TDerived>.Equals(TDerived other)
        {
            return Value.Equals(other.Value);
        }


        public override int GetHashCode()
        {
            return Value.GetHashCode();
        }

        #endregion

        #region IComparable Members

        int IComparable<TDerived>.CompareTo(TDerived other)
        {
            return Value.CompareTo(other.Value);
        }

        int IComparable.CompareTo(object obj)
        {
            if (obj != null)
            {
                if (obj is TValue)
                    return Value.CompareTo((TValue)obj);

                if (obj is TDerived)
                    return Value.CompareTo(((TDerived)obj).Value);
            }
            return -1;
        }

        int IComparer<TDerived>.Compare(TDerived x, TDerived y)
        {
            return (x == null) ? -1 :
                   (y == null) ? 1 :
                    x.Value.CompareTo(y.Value);
        }

        #endregion

        public static IEnumerable<TDerived> Values
        {
            get
            {
                return _values.Values;
            }
        }

        public static TDerived Parse(string name)
        {
            foreach (TDerived value in Values)
                if (0 == string.Compare(value.Name, name, true))
                    return value;

            return null;
        }
    }
}

Un exemple d'utilisation que j'ai exécuté en mono:

using System.ComponentModel;
using System;

namespace NMatrix
{    
    public sealed class MyEnum : RichEnum<int, MyEnum>
    {
        [Description("aap")]  public static readonly MyEnum my_aap   = new MyEnum(63000);
        [Description("noot")] public static readonly MyEnum my_noot  = new MyEnum(63001);
        [Description("mies")] public static readonly MyEnum my_mies  = new MyEnum(63002);

        private MyEnum(int value) : base (value) { } 
        public static implicit operator MyEnum(int value) { return Convert(value); }
    }

    public static class Program
    {
        public static void Main(string[] args)
        {
            foreach (var enumvalue in MyEnum.Values)
                Console.WriteLine("MyEnum {0}: {1} ({2})", (int) enumvalue, enumvalue, enumvalue.Description);
        }
    }
}

Produire la sortie

[mono] ~/custom/demo @ gmcs test.cs richenum.cs && ./test.exe 
MyEnum 63000: my_aap (aap)
MyEnum 63001: my_noot (noot)
MyEnum 63002: my_mies (mies)

Remarque: mono 2.6.7 nécessite un cast explicite supplémentaire qui n'est pas nécessaire lors de l'utilisation de mono 2.8.2 ...

sehe
la source
Utiliser .Single () pour obtenir l'attribut description n'est pas une bonne idée. S'il n'y a pas d'attribut, Single () lève une exception, SingleOrDefault () ne le fait pas.
kerem
@kerem bon point, je l'ai mis à jour (en utilisant FirstOrDefault, pour éviter de supposer qu'il n'y a qu'un seul attribut). Que ce soit ou non assumer de telles choses est « une bonne idée » (ou une mauvaise personne, car cette question) est bien sûr, en fonction du contexte
sehe
1
L' amour, mais je suis tombé sur un problème: sous Windows 7 / .NET 4.5 cette ligne des TDerived instance = (TDerived)field.GetValue(null);résultats en instanceêtre null. Il semble que le runtime Mono doit avoir un ordre d'initialisation de type différent de celui de .NET qui permet à cela de fonctionner. Déroutant! J'ai dû à la place déplacer ce code dans une méthode statique et l'appeler à partir de l'initialiseur de type dans la sous-classe.
agentnega
@agentnega Merci pour cet ajout. Cela pourrait aider quelqu'un.
sehe
@agentnega Je rencontre le même problème sur .net 4.5.1. Il semble «violer» la spécification C # b / c qu'il n'initialise pas la valeur avant la première utilisation - du moins pas lors de l'utilisation de la réflexion. J'ai implémenté une solution de contournement qui ne nécessite pas l'implication de la sous-classe ('TDerived'). @ sehe dois-je modifier votre réponse et ajouter la solution de contournement à votre réponse ou dois-je publier une nouvelle réponse?
BatteryBackupUnit
5

Vous ne pouvez pas déclarer de conversions implicites sur les types enum, car ils ne peuvent pas définir de méthodes. Le mot clé implicite C # se compile en une méthode commençant par 'op_', et cela ne fonctionnerait pas dans ce cas.

Igal Tabachnik
la source
4

Vous pourriez probablement, mais pas pour l'énumération (vous ne pouvez pas y ajouter de méthode). Vous pouvez ajouter une conversion implicite à votre propre classe pour permettre à une énumération d'y être convertie,

public class MyClass {

    public static implicit operator MyClass ( MyEnum input ) {
        //...
    }
}

MyClass m = MyEnum.One;

La question serait pourquoi?

En général .Net évite (et vous devriez aussi) toute conversion implicite où des données peuvent être perdues.

Keith
la source
3

Si vous définissez la base de l'énumération comme longue, vous pouvez effectuer une conversion explicite. Je ne sais pas si vous pouvez utiliser des conversions implicites car les énumérations ne peuvent pas avoir de méthodes définies sur elles.

public enum MyEnum : long
{
    one = 1,
    two = 2,
}

MyEnum number = MyEnum.one;
long i = (long)number;

De plus, sachez qu'une énumération non activée sera par défaut la valeur 0, ou le premier élément - donc dans la situation ci-dessus, il serait probablement préférable de définir zero = 0également.

Cendre
la source
5
Vous n'avez pas besoin de l' : longici; la conversion explicite fonctionnerait bien sans elle. La seule conversion implicite légale est zéro.
Marc Gravell
3
Non; l'énumération par défaut est Int32
Marc Gravell
1
Voir: enum Foo {A, B, C} Console.WriteLine (Enum.GetUnderlyingType (typeof (Foo)));
Marc Gravell
14
Pourquoi est-ce marqué comme réponse et a tant de points? Ce n'est PAS pertinent pour la question OP !!! Il parle de IMPLICIT Conversion ... La valeur ajoutée est nulle.
Mehdi LAMRANI
3
La question implique déjà que les castings explicites sont compris, la question équivaut à demander "Comment éviter de lancer explicitement?", Auquel CET article ne s'applique pas.
Kit10
2

les énumérations me sont largement inutiles à cause de cela, OP.

Je finis par faire des photos tout le temps:

la solution simple

Un exemple de problème classique est le jeu VirtualKey pour détecter les pressions sur les touches.

enum VKeys : ushort
{
a = 1,
b = 2,
c = 3
}
// the goal is to index the array using predefined constants
int[] array = new int[500];
var x = array[VKeys.VK_LSHIFT]; 

problème ici est que vous ne pouvez pas indexer le tableau avec l'énumération car il ne peut pas convertir implicitement enum en ushort (même si nous avons même basé l'énumération sur ushort)

dans ce contexte spécifique, les énumérations sont obsolètes par la structure de données suivante. . . .

public static class VKeys
{
public const ushort
a = 1,
b = 2, 
c = 3;
}
Steven Ventura
la source
1

J'ai contourné un problème avec la réponse de sehe lors de l'exécution du code sur MS .net (non Mono). Pour moi spécifiquement, le problème s'est produit sur .net 4.5.1 mais d'autres versions semblent également affectées.

Le problème

accéder à un public static TDervied MyEnumValuepar réflexion (via FieldInfo.GetValue(null)n'initialise pas ledit champ.

La solution de contournement

Au lieu d'attribuer des noms aux TDerivedinstances lors de l'initialisation statique, RichEnum<TValue, TDerived>cela se fait paresseusement lors du premier accès de TDerived.Name. Le code:

public abstract class RichEnum<TValue, TDerived> : EquatableBase<TDerived>
    where TValue : struct, IComparable<TValue>, IEquatable<TValue>
    where TDerived : RichEnum<TValue, TDerived>
{
    // Enforcing that the field Name (´SomeEnum.SomeEnumValue´) is the same as its 
    // instances ´SomeEnum.Name´ is done by the static initializer of this class.
    // Explanation of initialization sequence:
    // 1. the static initializer of ´RichEnum<TValue, TDerived>´ reflects TDervied and 
    //    creates a list of all ´public static TDervied´ fields:
    //   ´EnumInstanceToNameMapping´
    // 2. the static initializer of ´TDerive´d assigns values to these fields
    // 3. The user is now able to access the values of a field.
    //    Upon first access of ´TDervied.Name´ we search the list 
    //    ´EnumInstanceToNameMapping´ (created at step 1) for the field that holds
    //    ´this´ instance of ´TDerived´.
    //    We then get the Name for ´this´ from the FieldInfo
    private static readonly IReadOnlyCollection<EnumInstanceReflectionInfo> 
                            EnumInstanceToNameMapping = 
        typeof(TDerived)
            .GetFields(BindingFlags.Static | BindingFlags.GetField | BindingFlags.Public)
            .Where(t => t.FieldType == typeof(TDerived))
            .Select(fieldInfo => new EnumInstanceReflectionInfo(fieldInfo))
            .ToList();

    private static readonly SortedList<TValue, TDerived> Values =
        new SortedList<TValue, TDerived>();

    public readonly TValue Value;

    private readonly Lazy<string> _name;

    protected RichEnum(TValue value)
    {
        Value = value;

        // SortedList doesn't allow duplicates so we don't need to do
        // duplicate checking ourselves
        Values.Add(value, (TDerived)this);

        _name = new Lazy<string>(
                    () => EnumInstanceToNameMapping
                         .First(x => ReferenceEquals(this, x.Instance))
                         .Name);
    }

    public string Name
    {
        get { return _name.Value; }
    }

    public static implicit operator TValue(RichEnum<TValue, TDerived> richEnum)
    {
        return richEnum.Value;
    }

    public static TDerived Convert(TValue value)
    {
        return Values[value];
    }

    protected override bool Equals(TDerived other)
    {
        return Value.Equals(other.Value);
    }

    protected override int ComputeHashCode()
    {
        return Value.GetHashCode();
    }

    private class EnumInstanceReflectionInfo
    {
        private readonly FieldInfo _field;
        private readonly Lazy<TDerived> _instance;

        public EnumInstanceReflectionInfo(FieldInfo field)
        {
            _field = field;
            _instance = new Lazy<TDerived>(() => (TDerived)field.GetValue(null));
        }

        public TDerived Instance
        {
            get { return _instance.Value; }
        }

        public string Name { get { return _field.Name; } }
    }
}

qui - dans mon cas - est basé sur EquatableBase<T>:

public abstract class EquatableBase<T>
    where T : class 
{
    public override bool Equals(object obj)
    {
        if (this == obj)
        {
            return true;
        }

        T other = obj as T;
        if (other == null)
        {
            return false;
        }

        return Equals(other);
    }

    protected abstract bool Equals(T other);

    public override int GetHashCode()
    {
        unchecked
        {
            return ComputeHashCode();
        }
    }

    protected abstract int ComputeHashCode();
}

Remarque

Le code ci-dessus n'intègre pas toutes les fonctionnalités de la réponse originale de Mark !

Merci

Merci à Mark pour sa RichEnummise en œuvre et merci à sehe d' avoir apporté quelques améliorations!

BatteryBackupUnit
la source
1

J'ai trouvé une solution encore plus simple tirée d'ici /codereview/7566/enum-vs-int-wrapper-struct J'ai collé le code ci-dessous à partir de ce lien juste au cas où cela ne fonctionnerait pas à l'avenir.

struct Day
{
    readonly int day;

    public static readonly Day Monday = 0;
    public static readonly Day Tuesday = 1;
    public static readonly Day Wednesday = 2;
    public static readonly Day Thursday = 3;
    public static readonly Day Friday = 4;
    public static readonly Day Saturday = 5;
    public static readonly Day Sunday = 6;

    private Day(int day)
    {
        this.day = day;
    }

    public static implicit operator int(Day value)
    {
        return value.day;
    }

    public static implicit operator Day(int value)
    {
        return new Day(value);
    }
}
adminSoftDK
la source
1

J'ai créé cet utilitaire pour m'aider à convertir un Enum en PrimitiveEnum et PrimitiveEnum en byte, sbyte, short, ushort, int, uint, long, or ulong.

Donc, cela convertit techniquement toute énumération en une valeur primitive.

public enum MyEnum
{
    one = 1, two = 2
}

PrimitiveEnum number = MyEnum.one;
long i = number;

Voir commit sur https://github.com/McKabue/McKabue.Extentions.Utility/blob/master/src/McKabue.Extentions.Utility/Enums/PrimitiveEnum.cs

using System;

namespace McKabue.Extentions.Utility.Enums
{
    /// <summary>
    /// <see href="https://stackoverflow.com/q/261663/3563013">
    /// Can we define implicit conversions of enums in c#?
    /// </see>
    /// </summary>
    public struct PrimitiveEnum
    {
        private Enum _enum;

        public PrimitiveEnum(Enum _enum)
        {
            this._enum = _enum;
        }

        public Enum Enum => _enum;


        public static implicit operator PrimitiveEnum(Enum _enum)
        {
            return new PrimitiveEnum(_enum);
        }

        public static implicit operator Enum(PrimitiveEnum primitiveEnum)
        {
            return primitiveEnum.Enum;
        }

        public static implicit operator byte(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToByte(primitiveEnum.Enum);
        }

        public static implicit operator sbyte(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToSByte(primitiveEnum.Enum);
        }

        public static implicit operator short(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToInt16(primitiveEnum.Enum);
        }

        public static implicit operator ushort(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToUInt16(primitiveEnum.Enum);
        }

        public static implicit operator int(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToInt32(primitiveEnum.Enum);
        }

        public static implicit operator uint(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToUInt32(primitiveEnum.Enum);
        }

        public static implicit operator long(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToInt64(primitiveEnum.Enum);
        }

        public static implicit operator ulong(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToUInt64(primitiveEnum.Enum);
        }
    }
}
McKabue
la source
+1 J'ai un framework de jeu avec beaucoup de choses identifiées par uints pour lesquelles le jeu lui-même fait généralement des enums, mais le framework n'en sait rien. Devoir le faire (uint)lors de l'appel du framework était une douleur. Votre idée à l'envers fonctionne parfaitement. Au lieu de structstocker le Enum, j'ai un struct IdNumberqui stocke le uintmais convertit implicitement le Enums que le jeu utilise. Au lieu de taper les paramètres du framework comme uint, je peux les taper IdNumber, et le framework peut les transmettre en interne efficacement, même en faisant des opérations intégrales sur eux.
Kevin
-2

L'introduction de conversions implicites pour les types enum briserait la sécurité des types, donc je ne recommanderais pas de le faire. Pourquoi voudriez-vous faire ça? Le seul cas d'utilisation pour cela que j'ai vu est lorsque vous souhaitez placer les valeurs d'énumération dans une structure avec une disposition prédéfinie. Mais même dans ce cas, vous pouvez utiliser le type enum dans la structure et simplement dire au Marshaller ce qu'il doit en faire.

OregonGhost
la source
J'ai une utilisation pour la conversion implicite d'énumérations. Utilisation de SPMetal pour générer des classes LINQ to SharePoint dans plusieurs sites de la même collection de sites. Certaines de mes listes se trouvent dans un sous-site, d'autres dans un sous-site différent. En raison de la façon dont SPMetal génère le code, les colonnes de site utilisées dans plusieurs listes de la collection peuvent être définies dans plusieurs espaces de noms. Cependant, je dois convertir entre l'énumération de champ de choix dans un espace de noms et la même énumération dans un autre espace de noms. Une conversion implicite serait très utile.
Zarepheth