¿Podemos definir conversiones implícitas de enumeraciones en c #?

129

¿Es posible definir una conversión implícita de enumeraciones en c #?

algo que pueda lograr esto?

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

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

¿Si no, porque no?

Adam Naylor
fuente
2
Me gustaría hacer esto también. Tenemos una enumeración enum YesNo {Yes, No}que podría convertirse implícitamente en bool.
Coronel Panic
Observando que este concepto deshabilita la verificación de seguridad del tipo de compilador. A largo plazo, una taquigrafía de conversión explícita como un '~' final podría ser mejor.
crokusek
El enlace ya no es válido. ¿Podemos eliminar el enlace o volver a publicar el sitio web en algún lugar?
ワ イ き ん ぐ

Respuestas:

128

Hay una solucion. Considera lo siguiente:

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

Lo anterior ofrece conversión implícita:

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

Esto es bastante más trabajo que declarar una enumeración normal (aunque puede refactorizar algunos de los anteriores en una clase base genérica común). Puede ir aún más lejos haciendo que la clase base implemente IComparable e IEquatable, así como agregando métodos para devolver el valor de los atributos de descripción, los nombres declarados, etc.

Escribí una clase base (RichEnum <>) para manejar la mayoría del trabajo duro, lo que facilita la declaración de enumeraciones anterior 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 clase base (RichEnum) se enumera a continuación.

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;
        }
    }
}
marca
fuente
Se corrigió un pequeño error de protección en la publicación :-) Es un operador público estático implícito AccountStatus (valor de byte) {return Convert (value); } NO devuelve Convert (byte);
Mehdi LAMRANI
Hice esta compilación de clase base. ¿Te importa si edito los cambios?
Sehe
64
Esta solución puede ser "correcta" como ejercicio o para probar las habilidades de programación de alguien, pero, por favor, no haga esto en la vida real. No solo es excesivo, es improductivo, imposible de mantener y feo como el infierno. No necesita usar una enumeración solo por el hecho de hacerlo. Puede poner un reparto explícito o simplemente escribir una clase estática con constantes.
Trampa del
3
¿No es básicamente re-implementado Java enum?
Agent_L
2
Un problema importante es que no puede usar esas constantes de solo lectura estática en las instrucciones de cambio.
Ian Goldby
34

No puede realizar conversiones implícitas (excepto cero) y no puede escribir sus propios métodos de instancia; sin embargo, probablemente pueda escribir sus propios métodos de extensión:

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

Sin embargo, esto no te da mucho (en comparación con solo hacer un reparto explícito).

Una de las principales veces que he visto que la gente quiere esto es para hacer la [Flags]manipulación a través de genéricos, es decir, un bool IsFlagSet<T>(T value, T flag);método. Desafortunadamente, C # 3.0 no es compatible con operadores de genéricos, pero puede evitar esto usando cosas como esta , que hacen que los operadores estén totalmente disponibles con genéricos.

Marc Gravell
fuente
Sí, ese fue uno de mis más buscados para C # 4: stackoverflow.com/questions/138367/… y stackoverflow.com/questions/7244
Keith
@Keith - buen trabajo lo hizo, entonces ;-p El soporte dinámico / operador no llegó al CTP, pero tengo un equipo de prueba listo para rodar para comparar los dos enfoques para operadores con dinámica ( vs genéricos / Expresión) cuando llegue allí.
Marc Gravell
@Keith: es posible que desee darle un giro a la clase Operador en MiscUtil; Estoy bastante seguro de que hará la mayor parte de lo que quieras.
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
fuente
pero ¿por qué struct?
Konrad
1
No hay razón realmente. Podrías usar static class, supongo. No hay ninguna ventaja en discutir ninguno de los casos en el ILcódigo final .
Glenn Slayden
18

Adapté la excelente clase base genérica RichEnum de Mark.

Fijación

  1. una serie de problemas de compilación debido a la falta de bits de sus bibliotecas (en particular: los nombres para mostrar dependientes de los recursos no se eliminaron por completo; ahora lo están)
  2. la inicialización no fue perfecta: si lo primero que hiciste fue acceder a la propiedad .Values ​​estática de la clase base, obtendrías un NPE. Se corrigió esto al forzar a la clase base a forzar curiosamente ( CRTP ) la construcción estática de TDerived justo a tiempo durante CheckInitialized
  3. finalmente movió la lógica CheckInitialized a un constructor estático (para evitar la penalización de verificar cada vez, la condición de carrera en la inicialización multiproceso; tal vez esto era una imposibilidad resuelta por mi bala 1.?)

Felicitaciones a Mark por la espléndida idea + implementación, aquí está para todos ustedes:

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

Una muestra de uso que ejecuté 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);
        }
    }
}

Produciendo la salida

[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 requiere una conversión explícita adicional que no se requiere cuando se usa mono 2.8.2 ...

sehe
fuente
Usar .Single () para obtener el atributo de descripción no es una buena idea. Si no hay ningún atributo, Single () lanza una excepción, SingleOrDefault () no.
kerem
@kerem buen punto, lo actualicé (usando FirstOrDefault, para evitar asumir que solo hay un único atributo). Sea o no asumir ese tipo de cosas es 'una buena idea' (o un mal uno, para el caso) es, por supuesto, depende del contexto
sehe
1
Me encanta esto, pero me encontré con un problema: en Windows 7 / .NET 4.5 esta línea TDerived instance = (TDerived)field.GetValue(null);resulta instanceser null. Parece que el tiempo de ejecución Mono debe tener un orden de inicialización de tipo diferente al de .NET que permite que esto funcione. ¡Misterioso! En su lugar, tuve que mover ese código a un método estático y llamarlo desde el inicializador de tipo en la subclase.
agentnega
@agentnega Gracias por esa adición. Podría ayudar a alguien.
sehe
@agentnega Estoy experimentando el mismo problema en .net 4.5.1. Parece "violar" la especificación C # b / c, no inicializa el valor antes del primer uso, al menos no cuando se usa la reflexión. He implementado una solución alternativa que no requiere la participación de la subclase ('TDerived'). @ sehe ¿debería editar su respuesta y agregar la solución a su respuesta o debería publicar una nueva respuesta?
BatteryBackupUnit
5

No puede declarar conversiones implícitas en tipos de enumeración, porque no pueden definir métodos. La palabra clave implícita de C # se compila en un método que comienza con 'op_', y en este caso no funcionaría.

Igal Tabachnik
fuente
4

Probablemente podría, pero no para la enumeración (no puede agregarle un método). Puede agregar una conversión implícita a su propia clase para permitir que una enumeración se convierta en ella,

public class MyClass {

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

MyClass m = MyEnum.One;

La pregunta sería ¿por qué?

En general .Net evita (y usted también debería hacerlo) cualquier conversión implícita en la que se puedan perder datos.

Keith
fuente
3

Si define la base de la enumeración como larga, puede realizar una conversión explícita. No sé si puede usar conversiones implícitas ya que las enumeraciones no pueden tener métodos definidos en ellas.

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

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

Además, tenga en cuenta que una enumeración no inicializada tendrá como valor predeterminado 0 o el primer elemento, por lo que en la situación anterior probablemente sería mejor definirla zero = 0también.

Ceniza
fuente
55
No necesitas el : longaquí; la conversión explícita funcionaría bien sin ella. La única conversión implícita legal es cero.
Marc Gravell
3
No; la enumeración predeterminada es Int32
Marc Gravell
1
Ver: enum Foo {A, B, C} Console.WriteLine (Enum.GetUnderlyingType (typeof (Foo)));
Marc Gravell
14
¿Por qué está marcado como respuesta y tiene tantos puntos? ¡Esto NO es relevante para la pregunta de OP! Está hablando de la conversión IMPLÍCITA ... El valor agregado es nulo.
Mehdi LAMRANI
3
La pregunta ya implica que se entienden los lanzamientos explícitos, la pregunta es equivalente a preguntar "¿Cómo evito el envío explícito?", A lo que ESTA publicación no se aplica.
Kit10
2

los enumeradores son en gran medida inútiles para mí debido a esto, OP.

Termino haciendo fotos todo el tiempo:

la solución simple

El problema clásico de ejemplo es el conjunto VirtualKey para detectar pulsaciones de teclas.

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

El problema aquí es que no puede indexar la matriz con la enumeración porque no puede convertir implícitamente la enumeración en ushort (aunque incluso basamos la enumeración en ushort)

En este contexto específico, las enumeraciones están obsoletas por la siguiente estructura de datos. . . .

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

He solucionado un problema con la respuesta de sehe al ejecutar el código en MS .net (no Mono). Para mí, específicamente, el problema ocurrió en .net 4.5.1, pero otras versiones también parecen afectadas.

La cuestión

acceder a una public static TDervied MyEnumValuepor la reflexión (a través de FieldInfo.GetValue(null)qué no initialize dicho campo.

La solución

En lugar de asignar nombres a TDerivedinstancias sobre el inicializador estático, RichEnum<TValue, TDerived>esto se hace perezosamente en el primer acceso de TDerived.Name. El código:

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

que, en mi caso, se basa en 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

¡El código anterior no incorpora todas las características de la respuesta original de Mark !

Gracias

¡Gracias a Mark por proporcionar su RichEnumimplementación y gracias a sehe por proporcionar algunas mejoras!

BatteryBackupUnit
fuente
1

Encontré una solución aún más fácil tomada desde aquí /codereview/7566/enum-vs-int-wrapper-struct Pegué el siguiente código desde ese enlace en caso de que no funcione en el 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);
    }
}
adminSoftDK
fuente
1

Creé esta utilidad para ayudarme a convertir un Enum a PrimitiveEnum y PrimitiveEnum a byte, sbyte, short, ushort, int, uint, long, or ulong.

Entonces, esto técnicamente convierte cualquier enumeración en cualquiera de sus valores primitivos.

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

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

Ver commit en 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
fuente
+1 Tengo un marco de juego con muchas cosas identificadas por uints para las que el juego en sí suele hacer enum, pero el marco no sabe nada. Tener que hacerlo (uint)al llamar al marco fue un dolor. Tu idea al revés funciona perfectamente. En lugar de structalmacenar el Enum, tengo un struct IdNumberque almacena uintpero convierte de forma implícita el Enums que usa el juego. En lugar de escribir los parámetros del marco como uint, puedo escribirlos IdNumber, y el marco puede transmitirlos internamente de manera eficiente, incluso haciendo operaciones integrales en ellos.
Kevin
-2

La introducción de conversiones implícitas para los tipos de enumeración rompería la seguridad de los tipos, por lo que no recomendaría hacerlo. ¿Por qué querrías hacer eso? El único caso de uso para esto que he visto es cuando desea poner los valores de enumeración en una estructura con un diseño predefinido. Pero incluso entonces, puede usar el tipo de enumeración en la estructura y simplemente decirle al Marshaller qué debe hacer con esto.

OregonGhost
fuente
Tengo un uso para la conversión implícita de enumeraciones. Uso de SPMetal para generar clases de LINQ to SharePoint en varios sitios de la misma colección de sitios. Algunas de mis listas están en un subsitio, otras en un subsitio diferente. Debido a cómo SPMetal genera el código, las columnas del sitio utilizadas en múltiples listas de la colección pueden definirse en múltiples espacios de nombres. Sin embargo, necesito convertir entre la enumeración del campo de elección en un espacio de nombres a la misma enumeración en otro espacio de nombres. La conversión implícita sería muy útil.
Zarepheth