Podemos definir conversões implícitas de enumerações em c #?


129

É possível definir uma conversão implícita de enumerações em c #?

algo que poderia conseguir isso?

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

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

Se não, por que não?


2
Eu também gostaria de fazer isso. Temos uma enumeração enum YesNo {Yes, No}que pode implicitamente converter em bool.
Coronel Panic

Observando que esse conceito desabilita a verificação de segurança do tipo de compilador. A longo prazo, uma abreviação explícita de conversão como um '~' à direita pode ser melhor.
crokusek

O link não é mais válido - podemos remover o link ou republicar o site em algum lugar?
2171919

Respostas:


128

Existe uma solução. Considere o seguinte:

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

O acima oferece conversão implícita:

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

Isso é um pouco mais trabalhoso do que declarar uma enum normal (embora você possa refatorar alguns dos itens acima em uma classe base genérica comum). Você pode ir ainda mais longe, fazendo com que a classe base implemente IComparable & IEquatable, além de adicionar métodos para retornar o valor de DescriptionAttributes, nomes declarados, etc.

Eu escrevi uma classe base (RichEnum <>) para lidar com a maioria do trabalho pesado, o que facilita a declaração de enumerações acima para:

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

A classe base (RichEnum) está listada abaixo.

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

Corrigido um pequeno erro de ocultação na postagem :-) É o operador implícito estático público AccountStatus (valor de byte) {return Convert (value); } NÃO retorna Convert (byte);
Mehdi LAMRANI

Eu fiz essa classe básica compilar. Você se importa se eu editar as alterações?
29511

64
Essa solução pode estar "certa" como exercício ou para testar as habilidades de programação de alguém, mas, por favor, não faça isso na vida real. Não é apenas um exagero, é improdutivo, insustentável e feio como o inferno. Você não precisa usar uma enumeração apenas por isso. Você coloca uma conversão explícita ou apenas escreve uma classe estática com constantes.
Armadilha de

3
Não é basicamente reimplementado o Java enum?
Agent_L 21/03

2
Um grande problema é que você não pode usar essas constantes estáticas somente leitura nas instruções do switch.
Ian Goldby

34

Você não pode fazer conversões implícitas (exceto zero) e não pode escrever seus próprios métodos de instância - no entanto, provavelmente pode escrever seus próprios métodos de extensão:

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

Isso não dá muito, no entanto (comparado a apenas fazer um elenco explícito).

Uma das principais vezes que vi pessoas quererem isso é [Flags]manipular via genéricos - isto é, um bool IsFlagSet<T>(T value, T flag);método. Infelizmente, o C # 3.0 não suporta operadores de genéricos, mas você pode contornar isso usando coisas como esta , que tornam os operadores totalmente disponíveis com genéricos.


Sim, isso foi um dos meus mais queria para C # 4: stackoverflow.com/questions/138367/... e stackoverflow.com/questions/7244
Keith

@ Keith - bom trabalho que fez, então ;-p O suporte dinâmico / do operador não entrou no CTP, mas eu tenho uma plataforma de teste pronta para rolar para comparar as duas abordagens para operadores com dinâmica ( vs genéricos / Expressão) quando chegar lá.
Marc Gravell

@ Keith - você pode querer dar uma guinada na classe Operator no MiscUtil; Tenho certeza de que fará o que você quiser.
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";

mas por que struct?
Konrad

1
Não há realmente razão. Você poderia usar static class, suponho. Não há vantagem em argumentar em ambos os casos no ILcódigo final .
Glenn Slayden

18

Adaptei a excelente classe básica genérica RichEnum de Mark.

Fixação

  1. vários problemas de compilação devido à falta de bits de suas bibliotecas (principalmente: os nomes de exibição dependentes de recursos não foram completamente removidos; agora são)
  2. a inicialização não foi perfeita: se a primeira coisa que você fez foi acessar a propriedade .Values ​​estática da classe base, você obteria um NPE. Corrigido isso forçando a classe base a curiosa-recursivamente ( CRTP ) a construção estática do TDerived just in time durante CheckInitialized
  3. finalmente movi a lógica CheckInitialized para um construtor estático (para evitar a penalidade de verificar a cada vez, a condição de corrida na inicialização multithread; talvez essa fosse uma impossibilidade resolvida pelo meu marcador 1.?)

Parabéns a Mark pela excelente idéia + implementação, e aqui estão todos:

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

Uma amostra de uso que executei em 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);
        }
    }
}

Produzindo a saída

[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: o mono 2.6.7 requer uma conversão explícita extra que não é necessária ao usar o mono 2.8.2 ...


Usar .Single () para obter o atributo description não é uma boa ideia. Se não houver atributo, Single () lança uma exceção, SingleOrDefault () não.
kerem

@kerem bom ponto, eu atualizei (usando FirstOrDefault, para evitar assumir que há apenas um único atributo). Se assumir ou não essas coisas é 'uma boa ideia' (ou , por
sinal)

1
Amo isso, mas eu tive um problema: no Windows 7 / .NET 4.5 desta linha TDerived instance = (TDerived)field.GetValue(null);resulta em instancesendo null. Parece que o tempo de execução Mono deve ter uma ordem de tipo de inicialização diferente daquela do .NET que permite que isso funcione. Intrigante! Em vez disso, tive que mover esse código para um método estático e chamá-lo do inicializador de tipo na subclasse.
agentnega

@agentnega Obrigado por essa adição. Isso pode ajudar alguém.
21414 Ver as

@agentnega Estou com o mesmo problema no .net 4.5.1. Parece "violar" a especificação C # b / c, não inicializa o valor antes do primeiro uso - pelo menos não ao usar a reflexão. Implementei uma solução alternativa que não exige que a subclasse ('TDerived') esteja envolvida. @ sehe devo editar sua resposta e adicionar a solução alternativa à sua resposta ou devo postar uma nova resposta?
BatteryBackupUnit

5

Você não pode declarar conversões implícitas em tipos de enumeração, porque eles não podem definir métodos. A palavra-chave implícita em C # é compilada em um método começando com 'op_', e não funcionaria nesse caso.


4

Você provavelmente poderia, mas não para o enum (você não pode adicionar um método a ele). Você pode adicionar uma conversão implícita à sua própria classe para permitir que uma enum seja convertida nela,

public class MyClass {

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

MyClass m = MyEnum.One;

A pergunta seria por quê?

Em geral, o .Net evita (e você também deve) qualquer conversão implícita em que os dados possam ser perdidos.


3

Se você definir a base da enum como longa, poderá executar uma conversão explícita. Não sei se você pode usar conversões implícitas, pois as enumerações não podem ter métodos definidos nelas.

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

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

Além disso, lembre-se disso que uma enumeração não-inicializada será padronizada com o valor 0 ou o primeiro item - portanto, na situação acima, provavelmente seria melhor definir zero = 0também.


5
Você não precisa do : longaqui; a conversão explícita funcionaria bem sem ela. A única conversão implícita legal é zero.
Marc Gravell

3
Não; a enumeração padrão é Int32
Marc Gravell

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

14
Por que isso está marcado como resposta e tem tantos pontos? Isso NÃO é relevante para a questão do OP !!! Ele está falando sobre a conversão IMPLICIT ... O valor agregado é nulo.
Mehdi LAMRANI

3
A pergunta já implica que os elencos explícitos são entendidos, a questão é equivalente a perguntar "Como evito transmitir explicitamente?", Ao qual ESTA postagem não se aplica.
precisa saber é o seguinte

2

enums são em grande parte inúteis para mim por causa disso, OP.

Eu acabo fazendo fotos relacionadas o tempo todo:

a solução simples

Um exemplo clássico de problema é o VirtualKey configurado para detectar pressionamentos de tecla.

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

O problema aqui é que você não pode indexar a matriz com o enum porque ele não pode converter implicitamente enum em ushort (mesmo que tenhamos baseado o enum em ushort)

nesse contexto específico, as enumerações são obsoletas pela seguinte estrutura de dados. . . .

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

1

Eu resolvi um problema com a resposta de sehe ao executar o código no MS .net (não mono). Para mim, especificamente, o problema ocorreu no .net 4.5.1, mas outras versões também parecem afetadas.

O problema

acessando um public static TDervied MyEnumValuepela reflexão (via FieldInfo.GetValue(null)que não initialize disse campo.

A solução alternativa

Em vez de atribuir nomes a TDerivedinstâncias no inicializador estático, RichEnum<TValue, TDerived>isso é feito preguiçosamente no primeiro acesso de TDerived.Name. O 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; } }
    }
}

qual - no meu caso - se baseia em 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

O código acima não incorpora todos os recursos do Mark resposta original !

obrigado

Agradecemos a Mark por fornecer sua RichEnumimplementação e a ver por fornecer algumas melhorias!


1

Encontrei uma solução ainda mais fácil tirada daqui /codereview/7566/enum-vs-int-wrapper-struct Eu colei o código abaixo a partir desse link para o caso de não funcionar no 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

Criei este utilitário para me ajudar a converter um Enum em PrimitiveEnum e PrimitiveEnum embyte, sbyte, short, ushort, int, uint, long, or ulong .

Portanto, isso tecnicamente converte qualquer enum em qualquer valor primitivo.

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

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

Consulte commit em 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 Eu tenho uma estrutura de jogo com muitas coisas identificadas por uints que o próprio jogo geralmente cria enum, mas a estrutura não sabe nada. Ter (uint)que chamar a estrutura foi uma dor. Sua ideia ao contrário funciona perfeitamente. Em vez de structarmazenar o Enum, eu tenho um struct IdNumberque armazena o, uintmas implicitamente converte dos que Enumo jogo usa. Em vez de digitar os parâmetros da estrutura como uint, eu posso digitá-los IdNumber, e a estrutura pode transmiti-los internamente de maneira eficiente, inclusive fazendo operações integradas neles.
Kevin

-2

A introdução de conversões implícitas para tipos de enumeros violaria a segurança de tipos, portanto, eu não recomendo fazer isso. Por que você gostaria de fazer isso? O único caso de uso para isso que eu vi é quando você deseja colocar os valores de enumeração em uma estrutura com um layout predefinido. Mas, mesmo assim, você pode usar o tipo enum na estrutura e apenas dizer ao Marshaller o que ele deve fazer com isso.


Eu tenho um uso para conversão implícita de enumerações. Usando o SPMetal para gerar classes LINQ to SharePoint em vários sites do mesmo conjunto de sites. Algumas das minhas listas estão em um subsite, outras em um subsite diferente. Devido à forma como o SPMetal gera o código, as colunas do site usadas em várias listas da coleção podem ser definidas em vários espaços para nome. No entanto, preciso converter entre o campo de escolha enum em um espaço para nome para o mesmo enum em outro espaço para nome. A conversão implícita seria muito útil.
Zarepheth 28/08
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.