Um Enum deve começar com 0 ou 1?

136

Imagine que eu defini o seguinte Enum:

public enum Status : byte
{
    Inactive = 1,
    Active = 2,
}

Qual é a melhor prática para usar enum? Ele deve começar com 1o exemplo acima ou com 0(sem os valores explícitos) assim:

public enum Status : byte
{
    Inactive,
    Active
}
Acaz Souza
fonte
13
Você realmente precisa numerá-los explicitamente?
Yuck
164
As enums foram criadas apenas para que coisas como essa não fossem importantes.
BoltClock
9
@Daniel - arrgh não! É melhor usar um enum quando você pensa que um booleano fará do que usá-lo quando você está pensando em um enum.
AAT
22
@ Daniel por causa do valor FileNotFound, é claro
Joubarc
5
xkcd.com/163 se aplica a enum ainda melhor do que a índices de matriz.
precisa saber é o seguinte

Respostas:

161

Diretrizes de projeto de estrutura :

✔️ Forneça um valor zero em enumerações simples.

Considere chamar o valor como "Nenhum". Se esse valor não for apropriado para essa enum em particular, o valor padrão mais comum para a enum deve receber o valor subjacente de zero.

Diretrizes de projeto de estrutura / Projeto de enums de bandeira :

O EVITE usar valores de enumeração de zero, a menos que o valor represente "todos os sinalizadores sejam limpos" e seja nomeado adequadamente, conforme prescrito na próxima diretriz.

✔️ Nomeie o valor zero de enumerações de flag Nenhum. Para uma enumeração de sinalizador, o valor sempre deve significar "todos os sinalizadores são limpos".

Andrey Taptunov
fonte
28
Falha antecipadamente: se 'None' não for apropriado, mas não houver um valor padrão lógico, eu ainda colocaria um valor zero (e chamaria de 'None' ou 'Invalid') que não deve ser usado, apenas para que se um membro da classe dessa enumeração não for inicializado corretamente, o valor não inicializado poderá ser facilmente detectado e, nas switchinstruções, ele será direcionado para a defaultseção em que eu a InvalidEnumArgumentException. Caso contrário, um programa pode inadvertidamente continuar executando com o valor zero de uma enumeração, que pode ser válido e passar despercebido.
Allon Guralnek
1
@Allon Parece melhor fornecer apenas os valores de enum que você sabe que são válidos e, em seguida, verificar se há valores inválidos no setter e / ou construtor. Dessa forma, você saberá imediatamente se algum código não está funcionando corretamente, em vez de permitir que um objeto com dados inválidos exista por um tempo desconhecido e descobrir mais tarde. A menos que "Nenhum" represente um estado válido, você não deve usá-lo.
Wprl
@SoloBold: Isso soa como um caso em que você não esquece de inicializar um membro da classe enum em um construtor. Nenhuma quantidade de validação o ajudará se você esquecer de inicializar ou esquecer de validar. Além disso, existem classes simples de DTO que não possuem construtores, mas que dependem de inicializadores de objetos . Caçar esse inseto pode ser extremamente doloroso. No entanto, adicionar um valor de enumeração não usado cria uma API feia. Eu evitaria isso para APIs voltadas para consumo público.
Allon Guralnek
@ Allon Esse é um bom argumento, mas eu diria que você deve validar enums na função setter e jogar do getter se o setter nunca foi chamado. Dessa forma, você tem um ponto de falha e pode planejar em campo sempre com um valor válido, o que simplifica o design e o código. Meus dois centavos de qualquer maneira.
wprl 7/09/11
@SoloBold: Imagine uma classe DTO com 15 propriedades auto-implementadas. Seu corpo tem 15 linhas. Agora imagine essa mesma classe com propriedades regulares. São no mínimo 180 linhas antes de adicionar qualquer lógica de verificação. Esta classe é usada exclusivamente internamente apenas para fins de transferência de dados. Qual deles você prefere manter, uma classe de 15 linhas ou uma classe de mais de 180 linhas? A sucintibilidade tem seu valor. Ainda assim, ambos os nossos estilos estão corretos, são simplesmente diferentes. (Acho que é aqui que a AOP entra e vence os dois lados da discussão).
Allon Guralnek
66

Bem, acho que discordo da maioria das respostas que dizem para não numerá-las explicitamente. Eu sempre os numero explicitamente, mas isso ocorre porque, na maioria dos casos, acabo persistindo em um fluxo de dados em que eles são armazenados como um valor inteiro. Se você não adicionar explicitamente os valores e, em seguida, adicionar um novo valor, poderá interromper a serialização e não conseguir carregar com precisão os objetos persistentes antigos. Se você deseja fazer algum tipo de armazenamento persistente desses valores, eu recomendo definir explicitamente os valores.

pstrjds
fonte
9
+1, concordou, mas apenas no caso em que seu código depende do número inteiro por algum motivo externo (por exemplo, serialização). Em qualquer outro lugar, você deve apenas deixar a estrutura fazer seu trabalho. Se você confia nos valores inteiros internamente, provavelmente está fazendo algo errado (veja: deixe a estrutura fazer seu trabalho).
Matthew Scharley
3
Eu gosto de persistir no texto do enum. Torna o banco de dados muito mais utilizável.
Dave
2
Normalmente, você não precisa definir explicitamente os valores ... mesmo quando eles são serializados. basta adicionar SEMPRE novos valores no final. isso resolverá problemas de serialização. caso contrário, você pode precisar de um controle de versão do seu armazenamento de dados (por exemplo, um cabeçalho do arquivo que contém uma versão para mudar o comportamento ao ler / escrever valores) (ou ver memento padrão)
Beachwalker
4
@ Dave: A menos que você documente explicitamente que os textos enum são sagrados, você se prepara para o fracasso se um futuro programador decidir ajustar um nome para que fique mais claro ou conforme alguma convenção de nomenclatura.
Supercat
1
@pstrjds: acho que é uma troca estilística - o espaço em disco é barato, porém, o tempo gasto constantemente convertendo entre valores enum e um valor quando a pesquisa no banco de dados é relativamente cara (duplamente, se você tiver uma ferramenta de relatório configurada em um banco de dados ou algo semelhante) . Se você está preocupado com o espaço, com as versões mais recentes do SQL Server, você pode compactar um banco de dados, o que significa que 1000 ocorrências de "SomeEnumTextualValue" ocuparão pouco espaço. Claro que isso não funcionará para todos os projetos - é uma troca. Acho que a preocupação com a largura de banda cheira a otimização prematura, talvez!
Dave
15

Um Enum é um tipo de valor e seu valor padrão (por exemplo, para um campo Enum em uma classe) será 0 se não for inicializado explicitamente.

Portanto, você geralmente deseja ter 0 como uma constante definida (por exemplo, Desconhecida).

No seu exemplo, se você quiser Inactiveser o padrão, ele deverá ter o valor zero. Caso contrário, você pode considerar adicionar uma constante Unknown.

Algumas pessoas recomendaram que você não especifique explicitamente valores para suas constantes. Provavelmente é um bom conselho na maioria dos casos, mas há alguns casos em que você deseja fazer isso:

  • Enums de sinalizadores

  • Enums cujos valores são usados ​​em interoperabilidade com sistemas externos (por exemplo, COM).

Joe
fonte
Descobri que as enumerações de sinalizadores são muito mais legíveis quando você não define explicitamente os valores. - Muito menos propenso a erros, permitindo que o compilador faça a matemática binária para você. (ou seja, [Flags] enum MyFlags { None = 0, A, B, Both = A | B, /* etc. */ }é muito mais legível, do que [Flags] enum MyFlags { None = 0, A = 1, B = 2, Both = 3, /* etc */ }.)
BrainSlugs83
1
@ BrainSlugs83 - Não vejo como isso seria útil no caso geral - por exemplo [Flags] enum MyFlags { None=0, A, B, C } , resultaria [Flags] enum MyFlags { None=0, A=1, B=2, C=3 }, enquanto que para um enum de Flags você normalmente desejaria C = 4.
Joe
14

A menos que você tenha um motivo específico para alterá-lo, deixe enumerações com seus valores padrão, que começam em zero.

public enum Status : byte
{
    Inactive,
    Active
}
FishBasketGordo
fonte
6

Eu diria que a melhor prática é não numerá-los e deixá-los implícitos - o que começaria a partir de 0. Como está implícito a preferência de idioma que é sempre bom seguir :)

John Humphreys - w00te
fonte
6

Eu começaria um enum do tipo booleano com um 0.

A menos que "Inativo" signifique algo diferente de "Inativo" :)

Isso mantém o padrão para aqueles.

Mark Schultheiss
fonte
6

Eu diria que depende de como você os usa. Para sinalizar enum, é uma boa prática ter 0 como Nonevalor, assim:

[Flags]
enum MyEnum
{
    None = 0,
    Option1 = 1,
    Option2 = 2,
    Option3 = 4,
    All = Option1 | Option2 | Option3,
}

Quando é provável que seu enum seja mapeado para uma tabela de pesquisa de banco de dados, eu o iniciaria com 1. Não deve importar muito para código escrito profissionalmente, mas isso melhora a legibilidade.

Em outros casos, eu deixaria como está, sem importar se eles começam com 0 ou 1.

Michael Sagalovich
fonte
5

A menos que você tenha um bom motivo para usar os valores brutos, só deve usar valores implícitos e referenciá-los com Status.Activee Status.Inactive.

O problema é que você pode querer armazenar dados em um arquivo simples ou banco de dados, ou usar um arquivo simples ou banco de dados que outra pessoa criou. Se você estiver fazendo isso sozinho, faça com que a numeração se ajuste ao que o Enum é usado.

Se os dados não forem seus, é claro que você deseja usar o que o desenvolvedor original usou como esquema de numeração.

Se você planeja usar o Enum como um conjunto de sinalizadores, há uma convenção simples que vale a pena seguir:

enum Example
{
  None      = 0,            //  0
  Alpha     = 1 << 0,       //  1
  Beta      = 1 << 1,       //  2
  Gamma     = 1 << 2,       //  4
  Delta     = 1 << 3,       //  8
  Epsilon   = 1 << 4,       // 16
  All       = ~0,           // -1
  AlphaBeta = Alpha | Beta, //  3
}

Os valores devem ter potências de dois e podem ser expressos usando operações de deslocamento de bits. None, obviamente deveria ser 0, mas Allé menos obviamente -1. ~0é a negação binária de 0e resulta em um número que tem todos os bits definidos como 1, o que representa um valor de-1 . Para sinalizadores compostos (frequentemente usados ​​por conveniência), outros valores podem ser mesclados usando o bit a bit ou o operador |.

zzzzBov
fonte
3

Não atribua nenhum número. Basta usá-lo como deveria ser usado.

Hooch
fonte
3

Se não especificado, a numeração começa em 0.

É importante ser explícito, pois as enums geralmente são serializadas e armazenadas como int, não como uma string.

Para qualquer enumeração armazenada no banco de dados, sempre numeramos explicitamente as opções para evitar mudanças e reatribuição durante a manutenção.

De acordo com a Microsoft, a convenção recomendada é usar a primeira opção zero para representar um valor padrão não inicializado ou o mais comum.

Abaixo está um atalho para iniciar a numeração em 1 em vez de 0.

public enum Status : byte
{
    Inactive = 1,
    Active
}

Se você deseja definir valores de sinalizador para usar operadores de bit em valores de enumeração, não comece a numerar com o valor zero.

dru
fonte
2

Se você começar com 1, poderá facilmente contar suas coisas.

{
    BOX_THING1     = 1,
    BOX_THING2     = 2,
    BOX_NUM_THING  = BOX_THING2
};

Se você começar com 0, use o primeiro como um valor para coisas não inicializadas.

{
    BOX_NO_THING   = 0,
    BOX_THING1     = 1,
    BOX_THING2     = 2,
    BOX_NUM_THING  = BOX_THING2
};
Jonathan Cline IEEE
fonte
5
Desculpe Jonathan. Penso que esta sugestão é uma "velha escola" em minha mente (o tipo de convenção vem dos anos de baixo nível c). Isso é uma solução rápida para "incorporar" algumas informações adicionais sobre o enum, mas essa não é uma boa prática em sistemas maiores. Você não deve usar uma enumeração se precisar de informações sobre o número de valores disponíveis, etc. E quanto a um BOX_NO_THING1? Você daria a ele BOX_NO_THING + 1? As enums devem ser usadas para o que devem ser usadas: Valores (int) específicos representados por nomes "falantes".
Beachwalker
Hum. Você está assumindo que é da velha escola, porque eu usei todas as letras maiúsculas, acho, em vez do MicrosoftBumpyCaseWithLongNames. Embora eu concorde que é melhor usar iteradores do que loop até atingir uma definição enumerada de XyzNumDefsInMyEnum.
Jonathan Cline IEEE
Essa é uma prática terrível em C # de várias maneiras. Agora, quando você obtém uma contagem de suas enumerações da maneira correta, ou se você tentar enumerá-las da maneira correta, receberá um objeto duplicado extra. Também torna a chamada .ToString () potencialmente ambígua (estragando a serialização moderna), entre outras coisas.
BrainSlugs83
0

Primeiro, a menos que você esteja especificando valores específicos por um motivo (o valor numérico tem significado em outro lugar, por exemplo, o banco de dados ou o serviço externo), não especifique valores numéricos e deixe que sejam explícitos.

Em segundo lugar, você deve sempre ter um item de valor zero (em enumerações sem sinalizadores). Esse elemento será usado como o valor padrão.

Justin Niessner
fonte
0

Não inicie-os em 0, a menos que haja um motivo, como usá-los como índices para uma matriz ou lista, ou se houver outro motivo prático (como usá-los em operações bit a bit).

Você enumdeve começar exatamente onde precisa. Também não precisa ser seqüencial. Os valores, se explicitamente definidos, precisam refletir algum significado semântico ou consideração prática. Por exemplo, um enumde "garrafas na parede" deve ser numerado de 1 a 99, enquanto um enumpara potências de 4 provavelmente deve começar em 4 e continuar com 16, 64, 256 etc.

Além disso, a adição de um elemento com valor zero ao enumdeve ser feito apenas se representar um estado válido. Às vezes "nenhum", "desconhecido", "ausente" etc. são valores válidos, mas muitas vezes não são.

wprl
fonte
-1

Eu gosto de iniciar minhas enumerações em 0, pois esse é o padrão, mas também gosto de incluir um valor desconhecido, com o valor -1. Isso se torna o padrão e às vezes pode ajudar na depuração.

Tomas McGuinness
fonte
4
Idéia horrível. Como um tipo de valor, as enumerações são sempre inicializadas em zero. Se você tiver algum valor que represente desconhecido ou não inicializado, ele precisará ser 0. Você não pode alterar o padrão para -1, o preenchimento zero é codificado em todo o CLR.
Ben Voigt
Ah, eu não percebi isso. Geralmente, defino o valor de uma enumeração de valor / propriedade quando decalro / inicializo. Obrigado pelo ponteiro.
Tomas McGuinness