Possiamo definire conversioni implicite di enumerazioni in c #?


129

È possibile definire una conversione implicita di enum in c #?

qualcosa che potrebbe raggiungere questo obiettivo?

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

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

In caso contrario, perché no?


2
Vorrei fare anche questo. Abbiamo un enum enum YesNo {Yes, No}che potrebbe implicitamente convertirsi in bool.
Colonnello Panic,

Notando che questo concetto disabilita il controllo di sicurezza del tipo di compilatore. A lungo termine, una scorciatoia di conversione esplicita come una "~" finale potrebbe essere migliore.
crokusek,

Il collegamento non è più valido: possiamo rimuovere il collegamento o ripubblicare il sito Web da qualche parte?
ワ イ き ん ぐ

Risposte:


128

C'è una soluzione Considera quanto segue:

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;
    }
}

Quanto sopra offre una conversione implicita:

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

Questo è un po 'più di lavoro che dichiarare un enum normale (anche se puoi trasformare alcuni dei precedenti in una classe base generica comune). Puoi andare oltre facendo in modo che la classe base implementi IComparable e IEquatable, nonché aggiungendo metodi per restituire il valore di Attributi Descrizione, nomi dichiarati, ecc. Ecc.

Ho scritto una classe base (RichEnum <>) per gestire la maggior parte del lavoro grugnito, che semplifica la dichiarazione di enumerazioni sopra riportata a:

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 base (RichEnum) è elencata di seguito.

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;
        }
    }
}

Corretto un piccolo errore di offguard nel post :-) È l'operatore implicito statico pubblico AccountStatus (valore byte) {return Convert (valore); } NON restituisce Convert (byte);
Mehdi LAMRANI,

Ho creato questa compilation di base. Ti dispiace se modifico le modifiche?
visto il

64
Questa soluzione potrebbe essere "giusta" come esercizio o testare le capacità di programmazione di qualcuno ma, per favore, non farlo nella vita reale. Non solo è eccessivo, è improduttivo, non realizzabile e brutto da morire. Non è necessario utilizzare un enum solo per il gusto di farlo. Puoi inserire un cast esplicito o semplicemente scrivere una classe statica con costanti.
Trappola

3
Non è sostanzialmente reimplementato Java enum?
Agent_L

2
Un grosso problema è che non è possibile utilizzare quelle costanti di sola lettura statiche nelle istruzioni switch.
Ian Goldby,

34

Non puoi fare conversioni implicite (tranne zero) e non puoi scrivere i tuoi metodi di istanza, tuttavia, puoi probabilmente scrivere i tuoi metodi di estensione:

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();
    }
}

Questo non ti dà molto, però (rispetto al solo fare un cast esplicito).

Una delle volte in cui ho visto che la gente lo desidera è fare [Flags]manipolazioni tramite farmaci generici, ovvero un bool IsFlagSet<T>(T value, T flag);metodo. Sfortunatamente, C # 3.0 non supporta gli operatori di generici, ma puoi aggirare il problema usando cose come questa , che rendono gli operatori completamente disponibili con generici.


Sì, quello era uno dei miei più ricercati per C # 4: stackoverflow.com/questions/138367/… e stackoverflow.com/questions/7244
Keith

@Keith - buon lavoro, ce l'ha fatta, quindi ;-p Il supporto dinamico / operatore non è arrivato al CTP, ma ho un banco prova pronto per il confronto per confrontare i due approcci per gli operatori con dinamico ( vs generics / Expression) quando arriva lì.
Marc Gravell

@Keith - potresti voler dare alla classe Operator in MiscUtil un vortice; Sono abbastanza sicuro che farà la maggior parte di quello che vuoi.
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";

ma perché strutt?
Konrad,

1
Nessun motivo davvero. Potresti usare static classsuppongo. Non c'è alcun vantaggio a discutere di entrambi i casi nel ILcodice finale .
Glenn Slayden il

18

Ho adattato l'eccellente base di base RichEnum di Mark.

Fissaggio

  1. una serie di problemi di compilazione dovuti a bit mancanti dalle sue librerie (in particolare: i nomi visualizzati dipendenti dalla risorsa non sono stati completamente rimossi; ora sono)
  2. l'inizializzazione non era perfetta: se la prima cosa che facessi fosse accedere alla proprietà statica .Values ​​dalla classe base, otterrai un NPE. Risolto questo problema forzando la classe base a curiosamente ricorsivamente ( CRTP ) la costruzione statica di TDerived appena in tempo durante CheckInitialized
  3. finalmente spostato la logica CheckInitialized in un costruttore statico (per evitare la penalità del controllo ogni volta, le condizioni di gara sull'inizializzazione multithread; forse questa era un'impossibilità risolta dal mio proiettile 1.?)

Complimenti a Mark per la splendida idea + implementazione, ecco a voi tutti:

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 esempio di utilizzo che ho eseguito su 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);
        }
    }
}

Produrre l'output

[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)

Nota: mono 2.6.7 richiede un cast extra esplicito che non è richiesto quando si utilizza mono 2.8.2 ...


L'uso di .Single () per ottenere l'attributo description non è una buona idea. Se non è presente alcun attributo, Single () genera un'eccezione, SingleOrDefault () no.
kerem,

@kerem buon punto, l'ho aggiornato (usando FirstOrDefault, per evitare di presumere che ci sia un solo attributo). Indipendentemente dal fatto che assumere tali cose sia "una buona idea" (o una cattiva , del
resto

1
Adoro questo, ma ho TDerived instance = (TDerived)field.GetValue(null);riscontrato un problema: su Windows 7 / .NET 4.5 questa riga risulta instanceessere null. Sembra che il runtime Mono debba avere un ordine di inizializzazione del tipo diverso rispetto a quello .NET che gli consente di funzionare. Sconcertante! Ho dovuto invece spostare quel codice in un metodo statico e chiamarlo dall'inizializzatore del tipo nella sottoclasse.
Agentnega,

@agentnega Grazie per quell'aggiunta. Potrebbe aiutare qualcuno.
Sehe

@agentnega Sto riscontrando lo stesso problema su .net 4.5.1. Sembra "violare" la specifica C # in b / c, non inizializza il valore prima del primo utilizzo - almeno non quando si usa la riflessione. Ho implementato una soluzione alternativa che non richiede il coinvolgimento della sottoclasse ("TDerived"). @sehe dovrei modificare la tua risposta e aggiungere la soluzione alternativa alla tua risposta o devo pubblicare una nuova risposta?
BatteryBackupUnit

5

Non è possibile dichiarare conversioni implicite sui tipi di enum, poiché non possono definire metodi. La parola chiave implicita C # si compila in un metodo che inizia con 'op_' e in questo caso non funzionerebbe.


4

Probabilmente potresti, ma non per l'enum (non puoi aggiungere un metodo). Puoi aggiungere una conversione implicita alla tua classe per consentire a un enum di essere convertito in essa,

public class MyClass {

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

MyClass m = MyEnum.One;

La domanda sarebbe: perché?

In generale .Net evita (e dovresti anche tu) qualsiasi conversione implicita in cui i dati possono essere persi.


3

Se definisci la base dell'enum come long, puoi eseguire una conversione esplicita. Non so se è possibile utilizzare conversioni implicite poiché gli enum non possono avere metodi definiti su di essi.

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

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

Inoltre, tieni presente che un'enumerazione non inizializzata passerà automaticamente al valore 0 o al primo elemento, quindi nella situazione sopra sarebbe probabilmente meglio definire zero = 0anche.


5
Non hai bisogno del : longqui; la conversione esplicita funzionerebbe bene senza di essa. L'unica conversione implicita legale è zero.
Marc Gravell

3
No; l'enum predefinito è Int32
Marc Gravell

1
Vedi: enum Foo {A, B, C} Console.WriteLine (Enum.GetUnderlyingType (typeof (Foo)));
Marc Gravell

14
Perché questo è contrassegnato come risposta e ha così tanti punti? Questo NON è rilevante per la domanda OP !!! Sta parlando di IMPLICIT Conversion ... Il valore aggiunto è zero.
Mehdi LAMRANI,

3
La domanda implica già che i cast espliciti sono compresi, la domanda equivale a chiedere "Come posso evitare il cast esplicito?", A cui QUESTO post non si applica.
Kit10

2

gli enum sono in gran parte inutili per me per questo motivo, OP.

Finisco per fare foto relative sempre:

la soluzione semplice

classico esempio di problema è il set di chiavi virtuali per il rilevamento dei tasti premuti.

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]; 

il problema qui è che non puoi indicizzare l'array con l'enum perché non può implicitamente convertire enum in ushort (anche se abbiamo persino basato l'enum su ushort)

in questo specifico contesto, gli enum sono obsoleti dalla seguente struttura di dati. . . .

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

1

Ho risolto un problema con la risposta di sehe quando eseguivo il codice su MS .net (non Mono). Per me in particolare il problema si è verificato su .net 4.5.1 ma anche altre versioni sembrano interessate.

Il problema

Accedendo una public static TDervied MyEnumValueper riflessione (via FieldInfo.GetValue(null)non non inizializzazione detto campo.

La soluzione alternativa

Invece di assegnare nomi alle TDerivedistanze sull'inizializzatore statico di RichEnum<TValue, TDerived>questo viene fatto pigramente al primo accesso di TDerived.Name. Il codice:

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; } }
    }
}

che - nel mio caso - si basa su 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();
}

Nota

Il codice sopra riportato non include tutte le funzionalità della risposta originale di Mark !

Grazie

Grazie a Mark per aver fornito la sua RichEnumimplementazione e grazie a sehe per aver apportato alcuni miglioramenti!


1

Ho trovato la soluzione ancora più semplice presa da qui /codereview/7566/enum-vs-int-wrapper-struct Ho incollato il codice qui sotto da quel link nel caso in cui non funzionasse in futuro.

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);
    }
}

1

Ho creato questa utility per aiutarmi a convertire un Enum in PrimitiveEnum e PrimitiveEnum in byte, sbyte, short, ushort, int, uint, long, or ulong.

Quindi, questo tecnicamente converte qualsiasi enum in qualsiasi suo valore primitivo.

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

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

Vedi commit su 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);
        }
    }
}

+1 Ho un framework di gioco con molte cose identificate da uints che il gioco stesso di solito fa enum, ma il framework non ne sa nulla. Dover(uint) chiamare il framework è stato un dolore. La tua idea all'indietro funziona perfettamente. Invece di structmemorizzare il Enum, ho un struct IdNumberche memorizza il uintma converte implicitamente dal Enums il gioco utilizza. Invece di digitare i parametri del framework come uint, posso digitarli IdNumbere il framework può passarli internamente in modo efficiente, anche eseguendo operazioni integrali su di essi.
Kevin,

-2

L'introduzione di conversioni implicite per i tipi di enum infrange la sicurezza dei tipi, quindi non consiglierei di farlo. Perché vorresti farlo? L'unico caso d'uso per questo che ho visto è quando vuoi mettere i valori enum in una struttura con un layout predefinito. Ma anche allora, puoi usare il tipo enum nella struttura e dire al Marshaller cosa dovrebbe fare con questo.


Ho un uso per la conversione implicita di enum. Utilizzo di SPMetal per generare classi LINQ to SharePoint in più siti della stessa raccolta siti. Alcuni dei miei elenchi si trovano in un sito secondario, altri in un sito secondario diverso. A causa del modo in cui SPMetal genera il codice, le colonne del sito utilizzate in più elenchi della raccolta possono essere definite in più spazi dei nomi. Tuttavia, devo convertire tra l'enum del campo di scelta in uno spazio dei nomi e lo stesso enum in un altro spazio dei nomi. La conversione implicita sarebbe molto utile.
Zarepheth,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.