Esta é uma situação correta para usar uma constante?


42

Então, meu professor estava retornando algum feedback sobre um projeto no qual eu estava trabalhando. Ele encaixou algumas marcas para este código:

if (comboVendor.SelectedIndex == 0) {
  createVendor cv = new createVendor();
  cv.ShowDialog();
  loadVendors();
}

Isso está em um manipulador de "índice alterado" da caixa de combinação. É usado quando o usuário deseja criar um novo fornecedor, minha primeira opção (índice 0, que nunca muda) abre a caixa de diálogo "Criar um novo fornecedor". Portanto, o conteúdo da minha caixa de combinação acaba assim:

Create New Vendor...
Existing Vendor
Existing Vendor 2
Existing Vendor 3

O problema dele é com o código da primeira linha:

if (comboVendor.SelectedIndex == 0)

Ele afirma que o 0 deve ser uma constante e, na verdade, encaixou minhas marcas por causa disso. Ele afirma que eu não deveria usar literais no meu código.

O problema é que eu não entendo por que eu gostaria de tornar esse código nessa situação uma constante. Esse índice nunca mudará, nem é algo que você precisaria ajustar. Parece um desperdício de memória manter um único 0 na memória que é usado para uma situação muito específica e nunca muda.


32
Ele está sendo dogmático. Números mágicos são geralmente uma coisa boa a evitar. Eu acho que -1, 0 e 1 podem ser considerados exceções a essa regra. Além disso, uma constante como esta não seria necessário mais espaço do que o literal 0.
Dave Mooney

23
@DaveMooney: Você tem certeza de que não está tendo uma reação brusca aqui? É verdade que as coisas como o -1no str.indexOf(substr) != -1de " strcontém substr" é prefectly justificada. Mas aqui, o significado do 0 não é óbvio (qual a relação com a criação de um novo fornecedor?) Nem verdadeiramente constante (e se a maneira de criar um novo fornecedor mudar?).

62
você tem que aprender as regras antes de começar a quebrar as regras
Ryathal

12
Eu tive esse método falhar em mim inesperadamente. A lista foi classificada em ordem alfabética. Eu usei - - Criar novo - - para que os traços classificassem o primeiro e o índice codificado 0 para o método CreateNew. Alguém adicionou um item começando com uma única citação 'Meu item', que é classificada antes dos traços. Minha codificação física causou uma falha no programa na próxima vez que a lista foi carregada. Eu tive que modificar manualmente o arquivo de dados da lista para recuperar os dados do cliente.
Hand-E-Food,

14
Você pode usar int.Zeroem vez de fazê-lo feliz :)
Paul Stovell

Respostas:


90

A maneira correta real de fazer isso em C # é não depender da ordem dos ComboItems .

public partial class MyForm : Form
{
    private readonly object VENDOR_NEW = new object();

    public MyForm()
    {
        InitializeComponents();
        comboVendor.Items.Insert(0, VENDOR_NEW);
    }

    private void comboVendor_Format(object sender, ListControlConvertEventArgs e)
    {
        e.Value = (e.ListItem == VENDOR_NEW ? "Create New Vendor" : e.ListItem);
    }

    private void comboVendor_SelectedIndexChanged(object sender, EventArgs e)
    {
        if(comboVendor.SelectedItem == VENDOR_NEW)
        {
            //Special logic for selecting "create new vendor"
        }
        else
        {
            //Usual logic
        }
    }
}

22
Bem, se for C #, você não deve usar ALL_CAPS para constantes. As constantes devem ser PascalCased - stackoverflow.com/questions/242534/…
Groky

4
@Groky: Por que isso importa? Quem se importa com como ele nomeia suas constantes? Isso é 100% correto se ele usa ALL_CAPS de maneira consistente para constantes.
marco-Fiset

4
@marcof: Bem, se as constantes fazem parte de uma interface pública, ele deve seguir as diretrizes de nomenclatura da MS. Caso contrário, ele deve pelo menos aprender as melhores práticas desde o início.
Groky

2
Isso ainda está incorreto. Ele muda o problema de ter um literal inteiro (zero) para um literal de seqüência de caracteres (Criar novo fornecedor). Na melhor das hipóteses, o problema agora é 'e se o rótulo mudar' em vez de 'e se o índice mudar'.
Freiheit

7
@ Freiheit: incorreto. A constante de cadeia aqui é apenas para exibição; isso não afeta a lógica do programa. Ao contrário do uso de valores mágicos / seqüências de caracteres para armazenar o estado do programa, alterar essa sequência (ou adicionar / remover itens da lista) nunca poderia quebrar nada.
BlueRaja - Danny Pflughoeft 02/12

83

A ordem na caixa de combinação pode mudar. E se você adicionar outra opção como "Criar fornecedor especial ..." antes de "Criar novo vendedor ..."

A vantagem de usar uma constante é que, se houver muitos métodos que dependem da ordem da caixa de combinação, você precisará alterar apenas a constante e não todos os métodos, caso isso mude.

Usar uma constante também é mais legível que um literal.

if (comboVendor.SelectedIndex == NewVendorIndex)

A maioria dos idiomas compilados substituirá a constante no tempo de compilação, portanto, não há penalidade no desempenho.


5
Eu usaria um código descritivo no atributo value, em vez de confiar em um índice que pode mudar (mover a opção de criação para o final da lista, absolutamente quebrará o método constante). Então basta procurar esse código.
CaffGeek

1
@Chad Eu concordo em identificar controles pela ordem em uma GUI é muito frágil. Se a linguagem suportar adicionar um valor ao elemento GUI que pode ser consultado, eu usaria isso.
Michael Krussel 01/12/11

3
@ solução, porque essa é a convenção.
Ikke

3
@ Ikke Essa definitivamente não é a convenção. stackoverflow.com/questions/242534/…
SolutionYogi

1
Se estamos falando de c #, então NewVendorIndex É a convenção. É consistente com o restante do estilo .NET.
MaR

36

Essa situação que você descreve é ​​uma decisão judicial, pessoalmente eu não usaria uma se for usada apenas uma vez e já estiver legível.

A resposta real, porém, é que ele escolheu isso para lhe ensinar uma lição.

Não esqueça que ele é professor, o trabalho dele é ensinar a você a codificação e as melhores práticas.

Eu diria que ele está fazendo um bom trabalho, na verdade.

Claro que ele pode parecer um pouco absoluto, mas tenho certeza que você pensará novamente antes de usar números mágicos.

Além disso, ele se destacou o suficiente para você ingressar em uma comunidade on-line sobre programadores apenas para descobrir o que é considerado uma prática recomendada nessa situação.

Tiremos o chapéu para o seu professor.


+1 para indicar que, nesse caso, os meios justificam o resultado final
oliver-clare

@ Thanos- " Não se esqueça que ele é professor, seu trabalho é ensinar a você a codificação e as melhores práticas. " Esse nunca foi o caso do meu professor. Eu diria que o trabalho dele é ensinar a você o que o departamento considera importante quando o curso é criado.
Ramhound 02/12/19

13

[...] minha primeira opção (índice 0, que nunca muda) abre a caixa de diálogo "Criar um novo fornecedor".

O fato de você ter que explicar isso prova por que você deve usar uma constante. Se você introduzisse uma constante como NEW_VENDOR_DIALOG, seu código seria mais auto-explicativo. Além disso, os compiladores otimizam as constantes, para que não haja uma alteração no desempenho.

Escreva programas para programadores, não para compiladores. A menos que você esteja especificamente tentando otimizar, o que não parece ser o seu caso.


2
-1 por mencionar o desempenho. Mesmo que não seja otimizado, um computador pode executar bilhões de operações como essa antes que o usuário perceba.
Boris Yankov

3
O @Boris OP parecia preocupado com o desperdício de recursos, razão pela qual eu o mencionei. Não vejo como minha resposta seria menos correta por causa disso.
Kd

12

Ele afirma que o 0 deve ser uma constante e, na verdade, encaixou minhas marcas por causa disso.

Eu concordo. O uso de zero aqui é "mágico". Imagine que você está lendo este código pela primeira vez. Você não sabe por que zero é especial e o literal não diz nada sobre por que zero é especial. Se, em vez disso, você disse if(comboVendor.SelectedIndex == CreateNewVendorIndex), fica extremamente claro para o leitor iniciante o que o código significa.

Ele afirma que eu não deveria usar literais no meu código.

Essa é uma posição extrema; uma posição realista seria dizer que o uso de literais é uma bandeira vermelha que indica que o código pode não ser tão claro quanto poderia ser. Às vezes é apropriado.

O problema é que eu não entendo por que eu gostaria de tornar esse código nessa situação uma constante. Esse índice nunca mudará

O fato de nunca mudar é um excelente motivo para torná-lo constante . É por isso que constantes são chamadas constantes; porque eles nunca mudam.

nem é algo que você precisaria ajustar.

Sério? Você não vê nenhuma situação em que alguém queira alterar a ordem das coisas em uma caixa de combinação?

O fato de você poder ver um motivo pelo qual isso pode mudar no futuro é um bom motivo para não torná-lo constante. Em vez disso, deve ser um campo inteiro estático readonly não constante. Uma constante deve ser uma quantidade garantida para permanecer a mesma para sempre . Pi e o número atômico de ouro são boas constantes. Os números de versão não são; eles mudam todas as versões. O preço do ouro é obviamente uma constante terrível; isso muda a cada segundo. Faça apenas coisas constantes que nunca mudam .

Parece um desperdício de memória manter um único 0 na memória que é usado para uma situação muito específica e nunca muda.

Agora chegamos ao cerne da questão.

Essa talvez seja a linha mais importante em sua pergunta, pois indica que você tem algum entendimento profundamente falho de (1) memória e (2) otimização. Você está na escola para aprender e agora seria um ótimo momento para entender corretamente os fundamentos. Você pode explicar em detalhes por que acredita que "é um desperdício de memória manter um único zero na memória"? Primeiro, por que você acredita que otimizar o uso de quatro bytes de memória em um processo com pelo menos dois bilhões de bytes de armazenamento endereçável pelo usuário é relevante? Segundo, exatamente que recurso você imagina que está sendo consumido aqui ? O que você quer dizer com "memória" sendo consumida?

Estou interessado nas respostas a essas perguntas primeiro, porque elas são uma oportunidade para você aprender como seu entendimento de otimização e gerenciamento de memória está incorreto, e segundo porque eu sempre quero saber por que os iniciantes acreditam em coisas bizarras, para que eu possa projetar melhores ferramentas para levá-los a ter crenças corretas.


6

Ele tem razão. Você está certo. Você está errado.

Ele está certo, conceitualmente, de que os números mágicos devem ser evitados. As constantes tornam o código mais legível, adicionando contexto ao significado do número. No futuro, quando alguém ler seu código, saberá por que um número específico foi usado. E se você precisar alterar um valor em algum lugar abaixo da linha, é muito melhor alterá-lo em um só lugar, em vez de tentar procurar em qualquer lugar onde um número específico seja usado.

Dito isto, você está certo. Nesse caso específico, eu realmente não acho que uma constante se justifique. Você está procurando o primeiro item da lista, que é sempre zero. Nunca será 23. Ou -pi. Você está procurando especificamente zero. Realmente não acho que você precise desordenar o código, tornando-o constante.

Você está errado, no entanto, ao assumir que uma constante é carregada como uma variável, 'usando memória'. Existe uma constante para o humano e o compilador. Ele diz ao compilador para colocar esse valor nesse local durante a compilação, onde você teria colocado um número literal. E mesmo que tivesse a constante na memória, para todas as aplicações, exceto as mais exigentes, a perda de eficiência nem seria mensurável. Preocupar-se com o uso da memória de um único inteiro definitivamente cai na 'otimização prematura'.


1
Eu quase teria votado nessa resposta, se não fosse o terceiro parágrafo, alegando que o uso do literal 0 é suficientemente claro e nenhuma constante é justificada. Uma solução muito melhor teria sido não confiar na indexação do item da caixa de combinação, mas no valor do item selecionado. Constantes mágicas são constantes mágicas, mesmo que sejam 0 ou 3,14 ou outros enfeites - nomeie-as adequadamente, pois isso torna o código mais legível.
Roland Tepp

Pode ser melhor usar o valor da lista, mas não era disso que se tratava sua pergunta. Sua pergunta era se esse era um uso apropriado para uma constante. E se alguém estiver comparando a 0 no contexto da interface com a GUI (por qualquer motivo - talvez alguém esteja procurando o item principal independentemente do valor), acho que seria desnecessário usar uma constante nesse local.
GrandmasterB

Eu não concordaria ... Basta olhar para o código e nunca é óbvio imediatamente qual é o significado de 0 - pode ser que ele esteja sempre procurando o primeiro item da lista e é isso, mas é seria muito mais limpo, se a comparação fosse 'comboVendor.SelectedIndex == FirstIndex' em vez disso?
Roland Tepp

2

Eu teria substituído o 0com uma constante para tornar o significado claro, como NewVendorIndex. Você nunca sabe se seu pedido será alterado.


1

Essa é a preferência total do seu professor. Normalmente, você usa apenas uma constante se o literal for usado várias vezes, você deseja tornar óbvio para o leitor qual é o objetivo da linha, ou seu literal será possivelmente alterado no futuro e você só deseja alterar em um só lugar. No entanto, para este semestre, o professor é o chefe, então eu o faria a partir de agora nessa aula.

Bom treinamento para o mundo corporativo? Bem possível.


2
Não concordo com a parte "use apenas uma constante se o literal for usado várias vezes". Com 0 (e talvez -1, 1), muitas vezes fica claro; na maioria dos casos, é bom dar um nome a algo que é muito mais claro ao ler o código.
Johannes

1

Para ser sincero, embora eu não ache o seu código uma boa prática, a sugestão dele é francamente um pouco grotesca.

Uma prática mais comum para uma caixa de combinação .NET é atribuir ao item "Selecionar .." um valor vazio, enquanto os itens reais têm valores significativos e, em seguida:

if (string.IsNullOrEmpty(comboVendor.SelectedValue))

ao invés de

if (comboVendor.SelectedIndex == 0)

3
No seu exemplo, null é simplesmente outro literal. O cerne desta lição é que literais devem ser evitados.
overlacked

@overslacked - Não há literal nulo no meu exemplo.
precisa saber é o seguinte

Mesmo que houvesse um literal nulo, nulo é um literal aceitável para uso, nem acredito que haja uma maneira de verificar se uma referência a um objeto é nula sem usar a verificação se é igual a nulo.
Ramhound 02/12/19

Carson63000 - Eu estava me referindo ao seu uso de IsNullOrEmpty. Você está trocando um "valor mágico" por outro. @ Ramhound - Nulo é um literal aceitável em alguns casos, sem dúvida, mas eu não acredito que este seja um bom exemplo (usando nulo ou em branco como valor mágico).
overlacked

-1 para "um pouco grotesco". Enquanto você está certo de que usando o valor seria mais convencional e mais fácil de entender, se estiver usando o SelectedIndex, usando uma constante chamada é definitivamente melhor do que apenas 0.
jmoreno

1

Ele não está errado ao enfatizar o valor do uso de constantes e você não está errado ao usar literais. A menos que ele tenha enfatizado que este é o estilo de codificação esperado, você não deve perder marcas por usar literais, pois eles não são prejudiciais. Eu já vi literais usados ​​em todo o lugar tantas vezes no código comercial.

Seu argumento é bom. Este pode ser o seu caminho de conscientizá-lo dos benefícios das constantes:

1-Eles protegem seu código em certa medida contra violações acidentais

2-Como o @DeadMG diz em sua resposta, se o mesmo valor literal for usado em muitos lugares, ele pode aparecer com um valor diferente por engano - portanto, as constantes preservam a consistência.

3-Constantes preservam o tipo, para que você não precise usar algo como 0F para significar zero.

4-Para facilitar a leitura, COBOL usa ZERO como uma palavra reservada para o valor zero (mas também permite que você use o zero literal) - Portanto, atribuir um nome a um valor às vezes é útil, por exemplo: (Fonte: ms-Constants

class CalendarCalc
{
    const int months = 12;
    const int weeks = 52; //This is not the best way to initialize weeks see comment
    const int days = 365;

    const double daysPerWeek = (double) days / (double) weeks;
    const double daysPerMonth = (double) days / (double) months;
}

ou como no seu caso (conforme mostrado na resposta de Michael Krussel)


2
Isso não será uma definição estranha para dias da semana? Há 7 dias por semana, não 7,4287143
Winston Ewert

@WinstonEwert, obrigado pelo seu comentário. 365/52 = 7,01923076923077 = 7 + (1/52). Agora, se você remover a parte fracionária e calcular 7 * 52, receberá 364 dias, que não é o número correto de dias em um ano. Ter uma fração é mais preciso do que perdê-la (já que você pode formatar o resultado para mostrar 7 se quiser exibir apenas o número). Enfim, foi apenas um exemplo do MS sobre constantes, mas o seu ponto é interessante.
NoChance

1
É claro que é apenas um exemplo, mas objeto à sua afirmação de que é mais preciso incluir a fração. Uma semana é definida como 7 dias. Dada a sua definição, 26 semanas são 182,5 dias, o que simplesmente não é preciso. Realmente, o problema é seu int weeks = 52, não há 52 semanas por ano. Há 52,142857142857146 semanas em um ano, e esse é o número em que você deve manter as frações. Claro, a única coisa que é realmente constante em todo esse conjunto de constantes é o número de meses.
Winston Ewert

0

Você só precisaria colocá-lo em uma constante se tiver uma derivação complexa ou se repetir com frequência. Senão, um literal é bom. Colocar tudo em constante é um exagero total.


Somente se você considerar duas vezes com frequência e nunca precisará modificar o código. É realmente fácil criar bugs alterando um dos dois casos.
BillThor

0

Na verdade, como mencionado, e se a posição mudar? O que você pode / deve fazer é usar um código, em vez de confiar no índice.

Então, quando você cria sua lista de seleção, ela acaba com html como

<select>
    <option value='CREATE'>Create New Vendor...</option>
    <option value='1'>Existing Vendor</option>
    <option value='2'>Existing Vendor 2</option>
    <option value='3'>Existing Vendor 3</option>
</select>

Em vez de selectedIndex === 0procurar, verifique se o valor está CREATECODEem uma constante e teria sido usado para esse teste e ao criar a lista de seleção.


3
Provavelmente é uma boa abordagem, mas como ele está usando C #, o html não é o código de exemplo mais esperançoso.
Winston Ewert

0

Eu me livraria completamente disso. Basta colocar um botão criar novo ao lado da lista da caixa de combinação. Clique duas vezes em um item da lista para editar ou clique no botão Não tenha a nova funcionalidade enterrada na caixa de combinação. Em seguida, o número mágico é removido por completo.

Em geral, qualquer número literal no código deve ser definido como uma constante, a fim de contextualizar o número. O que significa zero? Nesse caso, 0 = NEW_VENDOR. Em outros casos, pode significar algo diferente; portanto, é sempre uma boa idéia para facilitar a leitura e a manutenção colocar algum contexto em torno disso.


0

Como já foi dito, você deve usar algum método diferente do número do índice para identificar o item da caixa de combinação que corresponde a uma determinada ação; ou, você pode encontrar o índice com alguma lógica programática e armazená-lo em uma variável.

A razão pela qual estou escrevendo é abordar seu comentário sobre o "uso da memória". Em C #, como na maioria dos idiomas, as constantes são "dobradas" pelo compilador. Por exemplo, compile o programa a seguir e examine a IL. Você verá que todos esses números nem chegam à IL, muito menos à memória do computador:

public class Program
{
    public static int Main()
    {
        const int a = 1000;
        const int b = a + a;
        const int c = b + 42;
        const int d = 7928345;
        return (a + b + c + d) / (-a - b - c - d);
    }
}

IL resultante:

.method public hidebysig static 
    int32 Main () cil managed 
{
    .maxstack 1
    .locals init (
        [0] int32 CS$1$0000
    )

    IL_0000: nop
    IL_0001: ldc.i4.m1  // the constant value -1 to be returned.
    IL_0002: stloc.0
    IL_0003: br.s IL_0005

    IL_0005: ldloc.0
    IL_0006: ret
}

Portanto, se você usa uma constante, um literal ou um kilobyte de código usando aritmética constante, o valor é tratado literalmente na IL.

Um ponto relacionado: a dobragem constante se aplica a literais de string. Muitos acreditam que uma chamada como essa causa muita concatenação desnecessária e ineficiente de strings:

public class Program
{
    public static int Main()
    {
        const string a = "a";
        const string b = a + a;
        const string c = "C";
        const string d = "Dee";
        return (a + b + c + d).Length;
    }
}

Mas confira o IL:

IL_0000: nop
IL_0001: ldstr "aaaCDee"
IL_0006: callvirt instance int32 [mscorlib]System.String::get_Length()
IL_000b: stloc.0
IL_000c: br.s IL_000e
IL_000e: ldloc.0
IL_000f: ret

Conclusão: os operadores em expressões constantes resultam em expressões constantes, e o compilador faz todo o cálculo; isso não afeta o desempenho em tempo de execução.

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.