Escolhendo o valor padrão de um tipo de enum sem precisar alterar valores

208

Em C #, é possível decorar um tipo de Enum com um atributo ou fazer outra coisa para especificar qual deve ser o valor padrão, sem que os valores sejam alterados? Os números necessários podem ser definidos por qualquer motivo, e seria útil ainda ter controle sobre o padrão.

enum Orientation
{
    None = -1,
    North = 0,
    East = 1,
    South = 2,
    West = 3
}

Orientation o; // Is 'North' by default.
xyz
fonte
Você deve poder alterar o valor padrão para int ?, double ?, etc.?
AR
2
@AR Sem ofensas, mas não faz sentido comparar enumerações com ints no resumo , simplesmente porque elas são implementadas como tipos numéricos. Poderíamos implementar enums como strings, e isso não mudaria sua utilidade. Considero a resposta aqui uma limitação da expressividade da linguagem C #; a limitação certamente não é inerente à idéia de "distinguir valores de um conjunto restrito".
21318 jpaugh
1
@jpaugh Não estou comparando enums com ints "no resumo". Estou dando exemplos de outros dados (incluindo seqüências de caracteres e outras referências) nos quais a alteração dos padrões não faz sentido. Não é uma limitação, é um recurso de design. A consistência é sua amiga :) #
224 AR #
@ARP ponto tomado. Suponho que passo muito tempo pensando em enums como construções em nível de tipo, especificamente como tipos de soma (ou veja aqui . Ou seja, considere cada enum como um tipo distinto, em que todos os valores desse tipo são distintos dos demais valores de enum. De acordo com essa mentalidade, é impossível que as enumeras compartilhem qualquer valor e, de fato, essa abstração se desintegra quando você considera que o padrão de 0pode ou não ser válido para uma determinada enumeração e é simplesmente excitado em cada enumeração.
jpaugh
Sim, eu estou familiarizado com sindicatos discriminados. Enums em C # não são isso, então não sei se é útil pensar nelas em um nível tão alto (e incorreto). Enquanto um 'tipo de soma' certamente tem suas vantagens, o mesmo acontece com enum (assumindo que valores apropriados sejam fornecidos). por exemplo, myColor = Colors.Red | Cores. Azul. Meu ponto principal é que, se você deseja um tipo de soma para c #, crie um, em vez de tentar redefinir o conceito de enum.
AR

Respostas:

349

O padrão para um enum(de fato, qualquer tipo de valor) é 0 - mesmo que esse valor não seja válido enum. Não pode ser alterado.

James Curran
fonte
13
Reformular a pergunta para pedir que o valor padrão para um int ser 42. Pense em como Daft que os sons ... A memória é apagada pelo tempo de execução antes de obtê-lo, enums são estruturas lembrar
ShuggyCoUk
3
@ DougS 1. Não, mas admitir que você estava errado é. 2. Ele não ganhou reputação com comentários positivos.
Aske B.
7
Os comentários me fizeram desconfiar dessa resposta, então testei e verifiquei que essa resposta definitivamente está correta.
precisa saber é o seguinte
1
@JamesCurran Mas quando não estou usando valores 0 para enumerações, encontro "Uma exceção do tipo 'System.NullReferenceException' ocorreu no MyProjectName.dll, mas não foi tratada no código do usuário. Informações adicionais: Referência de objeto não definida para uma instância de um objeto." erro. Como corrigi-lo? Nota: Eu uso um método de extensão, a fim de exibir descrições de enum, mas não tenho certeza como evitar o erro no método auxiliar (GetDescription) definida sobre esta página
Jack
64

O valor padrão de qualquer enumeração é zero. Portanto, se você deseja definir um enumerador como o valor padrão, defina esse como zero e todos os outros enumeradores como diferente de zero (o primeiro enumerador a ter o valor zero será o valor padrão para essa enumeração, se houver vários enumeradores com o valor zero).

enum Orientation
{
    None = 0, //default value since it has the value '0'
    North = 1,
    East = 2,
    South = 3,
    West = 4
}

Orientation o; // initialized to 'None'

Se seus enumeradores não precisarem de valores explícitos, verifique se o primeiro enumerador é o que você deseja que seja o enumerador padrão, pois "Por padrão, o primeiro enumerador tem o valor 0 e o valor de cada enumerador sucessivo é aumentado por 1. " ( Referência C # )

enum Orientation
{
    None, //default value since it is the first enumerator
    North,
    East,
    South,
    West
}

Orientation o; // initialized to 'None'
Imperador XLII
fonte
38

Se zero não funcionar como o valor padrão adequado, você poderá usar o modelo de componente para definir uma solução alternativa para a enumeração:

[DefaultValue(None)]
public enum Orientation
{
     None = -1,
     North = 0,
     East = 1,
     South = 2,
     West = 3
 }

public static class Utilities
{
    public static TEnum GetDefaultValue<TEnum>() where TEnum : struct
    {
        Type t = typeof(TEnum);
        DefaultValueAttribute[] attributes = (DefaultValueAttribute[])t.GetCustomAttributes(typeof(DefaultValueAttribute), false);
        if (attributes != null &&
            attributes.Length > 0)
        {
            return (TEnum)attributes[0].Value;
        }
        else
        {
            return default(TEnum);
        }
    }
}

e então você pode ligar para:

Orientation o = Utilities.GetDefaultValue<Orientation>();
System.Diagnostics.Debug.Print(o.ToString());

Nota: você precisará incluir a seguinte linha na parte superior do arquivo:

using System.ComponentModel;

Isso não altera o valor padrão do enum, mas fornece uma maneira de indicar (e obter) o valor padrão desejado.

David
fonte
Desculpe, isso não funciona para você. Você pode adicionar mais informações? você precisa adicionar a linha usando System.ComponentModel; e escreva a função GetDefaultEnum () em uma classe Utilitários. Qual versão do .net você está usando?
David
3
+1 para a reutilização do atributo do modelo de componente e a solução limpa. Eu gostaria de recomendar-lhe para mudar a última linha na elseinstrução da seguinte forma: return default(TEnum);. Primeiro, ele reduzirá as Enum.GetValues(...).***chamadas mais lentas , depois (e mais importante) será genérico para qualquer tipo, mesmo que não seja enum. Além disso, você não restringe o código a ser usado apenas para enumerações; portanto, isso corrige possíveis exceções quando usado em tipos não enumeráveis.
Ivaylo Slavov
1
Isso pode ser um uso indevido do atributo de valor padrão no contexto da pergunta. A pergunta é sobre o valor inicializado automaticamente, que não será alterado com a solução proposta. Este atributo é destinado ao padrão de um designer visual diferente do valor inicial. Não há menção pela pessoa que faz a pergunta do designer visual.
David Burg
2
Não há realmente nada na documentação do MSDN que o ComponentModel ou DefaultAttribute seja apenas um recurso "visual designer". Como afirma a resposta aceita. A resposta curta é "não, é impossível" redefinir o valor padrão de uma enumeração para algo diferente do valor 0. Esta resposta é uma solução alternativa.
David
17

O padrão de uma enumeração é qualquer enumeração igual a zero. Não acredito que isso possa ser alterado por atributo ou por outros meios.

(MSDN diz: "O valor padrão de uma enumeração E é o valor produzido pela expressão (E) 0.")

Joe
fonte
13
Estritamente, nem é preciso haver uma enumeração com esse valor. Zero ainda é o padrão.
Marc Gravell
11

Você não pode, mas se quiser, pode fazer algum truque. :)

    public struct Orientation
    {
        ...
        public static Orientation None = -1;
        public static Orientation North = 0;
        public static Orientation East = 1;
        public static Orientation South = 2;
        public static Orientation West = 3;
    }

uso dessa estrutura como enum simples.
onde você pode criar pa == Orientation.East (ou qualquer valor que você deseja) por padrão
para usar o truque em si, é necessário converter de int por código.
aí a implementação:

        #region ConvertingToEnum
        private int val;
        static Dictionary<int, string> dict = null;

        public Orientation(int val)
        {
            this.val = val;
        }

        public static implicit operator Orientation(int value)
        {
            return new Orientation(value - 1);
        }

        public static bool operator ==(Orientation a, Orientation b)
        {
            return a.val == b.val;
        }

        public static bool operator !=(Orientation a, Orientation b)
        {
            return a.val != b.val;
        }

        public override string ToString()
        {
            if (dict == null)
                InitializeDict();
            if (dict.ContainsKey(val))
                return dict[val];
            return val.ToString();
        }

        private void InitializeDict()
        {
            dict = new Dictionary<int, string>();
            foreach (var fields in GetType().GetFields(BindingFlags.Public | BindingFlags.Static))
            {
                dict.Add(((Orientation)fields.GetValue(null)).val, fields.Name);
            }
        } 
        #endregion
Avram
fonte
No seu operador de conversão implícita, você precisa value + 1; caso contrário, agora seus default(Orientation)retornos East. Além disso, torne o construtor privado. Boa abordagem.
Nawfal
10

Mais uma maneira que pode ser útil - usar algum tipo de "apelido". Por exemplo:

public enum Status
{
    New = 10,
    Old = 20,
    Actual = 30,

    // Use Status.Default to specify default status in your code. 
    Default = New 
}
Dmitry Pavlov
fonte
1
A sugestão é boa, no entanto, como já mencionado em outras respostas, as enumerações têm seu valor padrão como zero. No seu caso, a expressão Status s = default(Status);não será um dos membros do status, pois sterá o valor de (Staus) 0. O último é válido para escrever diretamente, pois as enumerações podem ser convertidas em números inteiros, mas falharão durante a desserialização, pois a estrutura garante que uma enumeração seja sempre desserializada para um membro válido. Seu código será perfeitamente válido se você alterar a linha New = 10para New = 0.
Ivaylo Slavov
1
Sim. Novo = 0 funcionaria. Eu pessoalmente evitaria não definir membros enum explicitamente.
Dmitry Pavlov
7

Na verdade enum, o padrão de um é o primeiro elemento em enumcujo valor é 0.

Então, por exemplo:

public enum Animals
{
    Cat,
    Dog,
    Pony = 0,
}
//its value will actually be Cat not Pony unless you assign a non zero value to Cat.
Animals animal;
Cian
fonte
9
No entanto, nesse caso, "Pony" É "Cat" (por exemplo, Console.WriteLine(Animals.Pony);imprime "Cat")
James Curran
16
Na verdade, você deve: nunca especificar nenhum valor OU especificar todos os valores OU especificar apenas o valor do primeiro membro definido. Aqui está o que acontece: Cat não tem valor e é o 1º valor, então o define automaticamente como 0. Dog não tem valor, define automaticamente como PreviousValue + 1 ie 1. 1. Poney tem um valor, deixe o valor. Então, Cat = Pony = 0, Dog = 1. Se você tiver {Cat, Dog, Pony = 0, Frog}, em seguida, Dog = Frog = 1.
user276648
4

The default value of enum is the enummember equal to 0 or the first element(if value is not specified) ... Mas enfrentei problemas críticos usando enum em meus projetos e superei fazendo algo abaixo ... Como sempre minha necessidade estava relacionada ao nível de classe ...

    class CDDtype
    {
        public int Id { get; set; }
        public DDType DDType { get; set; }

        public CDDtype()
        {
            DDType = DDType.None;
        }
    }    


    [DefaultValue(None)]
    public enum DDType
    {       
        None = -1,       
        ON = 0,       
        FC = 1,       
        NC = 2,       
        CC = 3
    }

e obtenha o resultado esperado

    CDDtype d1= new CDDtype();
    CDDtype d2 = new CDDtype { Id = 50 };

    Console.Write(d1.DDType);//None
    Console.Write(d2.DDType);//None

Agora, e se o valor vier do DB .... Ok, neste cenário ... passe o valor na função abaixo e ele converterá o valor em enum ... a função abaixo manipulou vários cenários e é genérica ... e acredite em mim muito rápido ..... :)

    public static T ToEnum<T>(this object value)
    {
        //Checking value is null or DBNull
        if (!value.IsNull())
        {
            return (T)Enum.Parse(typeof(T), value.ToStringX());
        }

        //Returanable object
        object ValueToReturn = null;

        //First checking whether any 'DefaultValueAttribute' is present or not
        var DefaultAtt = (from a in typeof(T).CustomAttributes
                          where a.AttributeType == typeof(DefaultValueAttribute)
                          select a).FirstOrNull();

        //If DefaultAttributeValue is present
        if ((!DefaultAtt.IsNull()) && (DefaultAtt.ConstructorArguments.Count == 1))
        {
            ValueToReturn = DefaultAtt.ConstructorArguments[0].Value;
        }

        //If still no value found
        if (ValueToReturn.IsNull())
        {
            //Trying to get the very first property of that enum
            Array Values = Enum.GetValues(typeof(T));

            //getting very first member of this enum
            if (Values.Length > 0)
            {
                ValueToReturn = Values.GetValue(0);
            }
        }

        //If any result found
        if (!ValueToReturn.IsNull())
        {
            return (T)Enum.Parse(typeof(T), ValueToReturn.ToStringX());
        }

        return default(T);
    }
Moumit
fonte
-1 por não responder intencionalmente à pergunta dada. Este não é um problema óbvio de lição de casa, que deve ser o único caso em que você intencionalmente não responde.
Xynariz
1
@Xynariz .. desculpe pessoal .. meus comentários foram negativos por engano, porque minha verdadeira intenção era fornecer uma solução de outra maneira ... Então mudei meus comentários ...: D
Moumit
2

O valor padrão para um tipo de enumeração é 0:

  • " Por padrão, o primeiro enumerador tem o valor 0 e o valor de cada enumerador sucessivo é aumentado em 1. "

  • " O tipo de valor enum tem o valor produzido pela expressão (E) 0, onde E é o identificador de enum. "

Você pode verificar a documentação para o C # enum aqui e a documentação para a tabela de valores padrão do C # aqui .

acoelhosantos
fonte
1

Se você definir a enumeração padrão como a enumeração com o menor valor, poderá usar isso:

public enum MyEnum { His = -1, Hers = -2, Mine = -4, Theirs = -3 }

var firstEnum = ((MyEnum[])Enum.GetValues(typeof(MyEnum)))[0];

firstEnum == Mine.

Isso não pressupõe que o enum tenha um valor zero.

Tal Segal
fonte
0

O padrão é o primeiro na definição. Por exemplo:

public enum MyEnum{His,Hers,Mine,Theirs}

Enum.GetValues(typeOf(MyEnum)).GetValue(0);

Isso retornará His

Tony Hopkinson
fonte
haha; O valor padrão é zero. MAS! ele diz que o valor padrão "zero" passa a ser o símbolo padrão do primeiro valor nomeado; ! por outras palavras, o idioma escolhe o padrão em vez de você; isto também só acontece que zero é o valor padrão de um int .. lol
user1953264
0
enum Orientations
{
    None, North, East, South, West
}
private Orientations? _orientation { get; set; }

public Orientations? Orientation
{
    get
    {
        return _orientation ?? Orientations.None;
    }
    set
    {
        _orientation = value;
    }
}

Se você definir a propriedade como null, retornará Orientations.None em get. A propriedade _orientation é nula por padrão.

Faruk A Feres
fonte
-5
[DefaultValue(None)]
public enum Orientation
{
    None = -1,
    North = 0,
    East = 1,
    South = 2,
    West = 3
}

Então no código você pode usar

public Orientation GetDefaultOrientation()
{
   return default(Orientation);
} 
Ruslan Urban
fonte
14
/! \ WRONG /! \ O DefaultValueAttribute não define realmente o valor, é apenas para fins informativos. Cabe a você verificar se sua enumeração, campo, propriedade ... possui esse atributo. O PropertyGrid usa-o para que você possa clicar com o botão direito do mouse e selecionar "Redefinir", também se não estiver usando o valor padrão, o texto será mostrado em negrito - MAS - você ainda precisará definir manualmente sua propriedade com o valor padrão desejado.
user276648