Lidar com sopa de chaves

11

Programei em C # e VB.NET por anos, mas principalmente em VB. Estou mudando de carreira para C # e, no geral, gosto mais de C #.

Um problema que estou tendo, no entanto, é a sopa de chaves. No VB, cada palavra-chave de estrutura possui uma palavra-chave estreita correspondente, por exemplo:

Namespace ...
    Class ...
        Function ...
            For ...
                Using ...
                    If ...
                        ...
                    End If
                    If ...
                        ...
                    End If
                End Using
            Next
        End Function
    End Class
End Namespace

O mesmo código escrito em C # acaba sendo muito difícil de ler:

namespace ... {
    class ... {
        function ... {
            for ... {
                using ... {
                    if ... {
                        ...
                    }
                    if ... {
                        ...
                    }
                }
            }
            // wait... what level is this?
        }
    }
}

Por estar tão acostumado com o VB, gostaria de saber se existe uma técnica empregada pelos programadores do estilo c para melhorar a legibilidade e garantir que seu código acabe no "bloco" correto. O exemplo acima é relativamente fácil de ler, mas às vezes no final de um código eu tenho 8 ou mais níveis de chaves, exigindo que eu role várias páginas para descobrir qual chave termina o bloco em que estou interessado dentro.

JDB ainda se lembra de Monica
fonte
83
Eu sei que isso pode parecer pragmático, e talvez você tenha condições especiais que exijam isso (porque sim, às vezes é necessário - felizmente esses tempos devem ser raros), mas geralmente "... 8 ou mais níveis de aparelho, exigindo que eu role várias páginas para descobrir em que chave termina o bloco em que estou interessado " significa que o código precisa de uma refatoração e limpeza sérias.
FrustratedWithFormsDesigner
7
Uma coisa que eu já vi fazer, e que comecei a fazer, é no final de uma chave, vou adicionar um comentário sobre isso. Algo como // End's using X statement.
PiedVenom 07/11
14
O @FrustratedWithFormsDesigner é direto sobre o fato de que seu fluxo de controle é uma bagunça e precisa ser refeito. Dito isto, acho que você está reclamando mais sobre os aparelhos do que sobre o escopo e eu diria que você só precisa se acostumar com isso. Aprender um idioma com sintaxe significativamente diferente da que você está acostumado definitivamente parece sopa por um tempo, mas com a prática que desaparece. Você só precisa se curvar e lidar até que seu cérebro comece a processar a sintaxe de forma mais natural, e você chegará lá.
Jimmy Hoffa
2
@FrustratedWithFormsDesigner - Preciso limpar muitos objetos COM em uma situação de interoperabilidade COM. Estou usando a técnica sugerida neste artigo: jake.ginnivan.net/vsto-com-interop . Isso cria facilmente duas ou três camadas. Quando você empilha o loop for, a função, a classe e o namespace em cima disso (junto com uma instrução if), você obtém facilmente várias camadas de chaves.
JDB ainda se lembra de Monica
5
@TyrionLannister - E esses podem ser alguns dos comentários mais rápidos para ficar fora de sincronia com o que eles pertencem ... Eu acho que se eu tivesse algo assim, eu preferiria que fosse gerado automaticamente (na exibição somente tempo, não persistido) pelo IDE.
Clockwork-Muse

Respostas:

38

Coloque sua chave inicial na mesma "classificação" que a final, assim:

namespace ... 
{
    class ... 
    {
        function ... 
        {
            for ... 
            {
                using ... 
                {
                    if ... 
                    {
                        ...
                    }
                    if ... 
                    {
                        ...
                    }
                }
            }
            // It's the `function` level!
        }
    }
}
Robert Harvey
fonte
15
Concordo. Parênteses egípcios me dão dor de cabeça.
PiedVenom 07/11
8
Além disso, a maioria dos IDEs (provavelmente) destaca o parceiro de uma chave quando você clica nela.
StuperUser
3
@TyrionLannister: Obrigado por finalmente me dar um termo para esse estilo! Eu nunca tive um bom nome para eles além de "aparelho desalinhado".
FrustratedWithFormsDesigner
3
@ Cyborgx37: Seu IDE possui o recurso "Ir para a chave correspondente"? Geralmente vinculado a um atalho de tecla que move automaticamente o cursor para a chave que corresponde à destacada no momento.
FrustratedWithFormsDesigner
3
Tenho que dizer, não vejo como isso facilita as coisas. De qualquer maneira, basta procurar a tela no nível de indentação da chave até chegar a uma palavra-chave. E com todas essas linhas extras, agora você precisa procurar mais.
Blorgbeard saiu em 07/11/12
14
  • Dependendo do seu IDE: coloque o cursor na chave de abrir / fechar e ela destacará isso e a chave correspondente.
  • Recolha o bloco e ele mostra onde ele abre / fecha.
  • Escreva blocos de código menores. Seriamente. Confira Clean Codee nunca mais encontre esse problema (e tenha um código mais legível / de manutenção).

Uma observação: o seguinte é uma sintaxe c # válida que pode ajudar sua situação específica:

using (var type = new MyDisposable1())
using (var type2 = new MyDisposable2())
{
    /* do what you will with type2 and type2 */
}
Steven Evers
fonte
Caçar o único personagem destacado foi o que me levou a postar essa pergunta em primeiro lugar.
JDB ainda se lembra de Monica
2
@ Cyborgx37: Daí o ponto 3. Se todo o seu bloco de código couber na tela, você nunca precisa caçar. Na maioria / todas as classes que escrevo, os únicos pares de chaves que não cabem na tela são namespace / classe.
Steven Evers
O que você sugere nos pontos 1 e 2 é o que faço agora ... mas isso exige que eu abandone o local em que estou codificando e comece a manipular minha visão do código para descobrir onde colocar a próxima linha. O ponto 3 é bem tomada, mas nem sempre é possível, especialmente se o seu código requer várias camadas de usingblocos (veja jake.ginnivan.net/vsto-com-interop )
JDB ainda se lembra Monica
@ Cyborgx37: Veja minha edição.
Steven Evers
1
@ Cyborgx37 Se você escolher uma cor que se destaca de todo o resto (usei fundo roxo e texto em branco por um tempo, IIRC), então não há necessidade de procurar a chave correspondente - ela praticamente grita para você "Estou aqui ! "
um CVn
5

Uma convenção comum é adicionar um comentário após a chave de fechamento para indicar a estrutura que está sendo fechada:

if {
   ...
} // end if

while (condition) {
   ...
} // end while

etc. Eu nunca me esqueci dessa convenção, mas algumas pessoas acham útil.

John Bode
fonte
16
Vi pessoas fazerem isso e, quando trocam / alteram o início do bloco (alteram a whilepara a for, trocam para ifinstruções), quase NUNCA se lembram de atualizar os comentários finais, tornando-os piores do que inúteis. Essa convenção provavelmente só será útil se você forçar a manter os comentários sempre que a natureza da correspondência for {alterada.
FrustratedWithFormsDesigner
Sim, eu fiz um pouco disso, mas é muito trabalho extra (e barulho). Eu esperava que houvesse algo mais direto.
JDB ainda se lembra de Monica
5
Os comentários devem explicar apenas por que nunca o que ou como o código faz os dois, e depender de comentários para explicar qualquer um deles significa que o código é difícil de ler, o que é um sinal de que o código deve ser corrigido e não comentado.
Jimmy Hoffa
1
Isso me faz pensar por que ninguém tem escrito um editor que exibe esses comentários, mas na verdade não incluí-los no código ..
Brendan Longo
2
@ JimmyHoffa: Eu acho que uma regra melhor para comentários é que eles devem fornecer clareza . Geralmente isso significa responder "por que", mas também pode significar outras coisas. Não fique tão envolvido com o dogma que ele o impede de fazer coisas que realmente ajudam, como ocasionalmente adicionar um comentário a uma chave de fechamento que está muito distante da chave de abertura.
Bryan Oakley #
5

Em geral, quando é difícil combinar chaves entre qualquer estilo - provavelmente significa que o método é muito longo e deve ser re-fatorado.

MaximR
fonte
5

Eu acho que você precisa resistir com o aparelho. Eventualmente, eles se tornarão uma segunda natureza para você e você estará se perguntando como você viveu sem eles.

Porém, verifique se eles estão recuados adequadamente e se alguma convenção de espaçamento está sendo seguida (não importa qual).

MrFox
fonte
Um ano depois, e esse conselho soa verdadeiro. :)
JDB ainda se lembra de Monica
4

Eu removo 2 níveis de aninhamento, recolhendo o espaço para nome e os escopos de classe horizontalmente. Observe que os métodos estão alinhados com a borda esquerda da tela. Não vejo sentido em perder 2 níveis de recuo em cada arquivo.

Depois disso, é raro que você tenha um aninhamento com mais de 4 níveis de profundidade.

namespace FooNameSpace {
class Foo {

public void bar()
{
    while(true)
    {
        while(true)
        {
            break;
        }
    }
}

public void fooBar()
{
    foreach(var item in FooList)
    {
        foreach(var b in item.Bars)
        {
            if(b.IsReady)
            {
                bar();
            }
            bar();
        }
        bar();
    }
}

}}//end class, namespace
mike30
fonte
Eu gosto dessa idéia, mas o Visual Studio parece não suportá-la (pelo menos, 2008. Estamos atualizando para 2012 até o final do ano, então aqui está a esperança)
JDB ainda se lembra de Monica
@ Cyborgx37. Eu edito o texto externamente no VIM, para que não seja um problema. Mas, no Visual Studio, faça: Control + A e pressione o botão "recuar menos". Faça isso apenas para novos arquivos. Não se preocupe com os arquivos existentes, pois isso atrapalha as comparações de diferenças no controle de origem.
precisa saber é o seguinte
Digitar colchetes / chaves de fechamento inicia um formato automático pelo VS, mas você sempre pode pressionar CTRL + z para recusar sua sugestão.
11158 Alex Em Paris
1

Decidi recentemente tentar formalizar duas regras sobre construções de controle de fluxo que basicamente são assim:

  • Você deve ter apenas construções de fluxo de código necessárias
  • Você deve tornar as construções de fluxo de código tão pequenas quanto possível

Por exatamente os motivos que você mencionou e está claramente ciente, acho que essas são ótimas regras a serem seguidas. Existem algumas técnicas simples que você pode empregar para realizá-las:

  • Saia do escopo assim que possível (isso inclui o escopo de loops e também de funções)
  • Observe os elses que podem ser mitigados ao sair da função do if anterior e aplique a técnica de saída do escopo, como mencionei
  • Inverta suas verificações condicionais quando o código dentro de um if for grande do que fora
  • Fatorar o código dentro de um loop para outro método quando o tamanho do loop aumentar para obscurecer o restante do método
  • Observe se há escopos que contenham apenas outro escopo, por exemplo, uma função cujo escopo inteiro seja preenchido por um if sem nada fora do if

Eu detalhei aqui exemplos de como não seguir esses procedimentos pode acabar fazendo o que você disse e colocando o código no bloco de código errado, o que é ruim e uma causa fácil de erros durante a manutenção.

Jimmy Hoffa
fonte
Obrigado, pode haver um ou dois blocos que eu possa refatorar. Isso ajudará, mas é difícil refatorar vários usingblocos aninhados .
JDB ainda se lembra de Monica '
@ Cyborgx37 realmente usando blocos pode ser embutido se eles estiverem aninhados em escopos satisfatórios, veja aqui stackoverflow.com/questions/1329739/… basicamente funciona como construções de controle de fluxo de linha única como você pode se (verdadeiro) doSomething (); você também pode if (true) if (elseElse) if (otherThings) {doThis (); faça isso(); doWhatever (); } Eo ninho ifs como você espera (não escrever o código assim pelo amor de Deus, basta fazer isso com usings E NADA MAIS heh)
Jimmy Hoffa
Sim, eu sei sobre aninhamento usando blocos, mas isso só funciona se você tiver uma única linha abaixo. Na maior parte do meu código, esse não é o caso. Ainda um post muito útil geral ... aprendi algumas coisas (+1)
JDB ainda se lembra Monica
@ Cyborgx37 sim, eu sei que o aninhamento de escopo só funciona se você não tiver bits extras, ou seja, existe um padrão na maneira como você usa? Seria talvez viável transferir parte desse extra para o construtor, se estiver no topo ou o descarte, no fundo? Ou seja, se for padronizado; supondo que possa ter ocorrido desde que você trouxe isso à tona como um problema; portanto, você provavelmente os encontrará usando ninhos frequentemente em seu código.
Jimmy Hoffa
Na verdade, comecei a trabalhar em uma implementação de padrão de fábrica que agrupa objetos COM em uma classe "AutoCleanup". Em seguida, uso uma única usinginstrução na classe de fábrica e, quando descartada, ela descarta automaticamente todas as classes agrupadas. Está funcionando bem até agora e reduziu significativamente o número de usinginstruções no meu código.
JDB ainda se lembra de Monica
1

Esta é uma das causas mais antigas de guerra na computação, infelizmente. Argumentos razoáveis ​​podem ser feitos de ambos os lados (melhor economia imobiliária vertical versus capacidade mais fácil de combinar visualmente a chave de abertura com a chave de fechamento), mas, na realidade, um simples formatador de código-fonte resolverá tudo para você. O MS Visual C # possui um que funciona bem.

Entretanto, esteja avisado de que se você estiver trabalhando como parte de uma equipe, deverá obedecer às convenções usadas por essa equipe; portanto, vale a pena ganhar alguma familiaridade com os dois estilos e abster-se de se tornar religioso.

Portanto, enquanto você estiver aprendendo, enfoque o estilo que facilita o aprendizado, mas fique de olho no outro enquanto estiver trabalhando e você se sairá bem.

Maximus Minimus
fonte
1
Não tenho certeza se é o formatador padrão ou algo do ReSharper, mas quando eu costumava usar C #, tínhamos um conjunto de opções que reformatava o código como parte do check-in. Dessa forma, você poderia formatar o código da maneira que desejasse enquanto trabalhava, mas seria reformatado para "padrão do projeto" no check-in. Eu acho que o único padrão de formatação real que tínhamos era usar espaços em vez de tabulações.
TMN
1

Use o Resharper, que ajudará a recomendar maneiras de reduzir o aninhamento. Leia também o livro Clean Code , de Bob Martin , que enfatiza que uma função deve fazer apenas uma coisa e, portanto, cada função deve ter apenas meia dúzia de linhas, para que você não tenha tantos níveis de aninhamento com os quais se preocupar.

lorddev
fonte
1

Há um complemento para o editor que pode ajudá-lo em: Estrutura de tópicos do C # .

O complemento estende o editor VS20xx para C # adicionando recursos para recolher, expandir e destacar blocos de código aninhados. Esses recursos permitem edição e leitura mais fáceis do conteúdo aninhado de blocos de código, como se, enquanto, etc.

NoChance
fonte
0

Se você escrever seu código no Visual Studio, também haverá um plug-in que mostra pontos verticais entre o início e o final de cada estrutura que você constrói.

Mas, no geral, acho que levará algum tempo até que você esteja acostumado à "Sopa de chaves". (Aliás, eu realmente gosto dessa expressão. Parece um nome de episódio para The Big Bang Theory)

mhr
fonte
0

O recuo informa onde você está, nos dois estilos de sintaxe. Se você escrever um programa VB ou um programa C # em uma única linha, em breve não poderá saber onde está na sintaxe aninhada. A máquina analisa as frases finais do bloco ou chaves, mas os humanos precisam de indentação.

As frases de final de bloco vêm de uma era de cartões perfurados e fita de papel, quando a programação era muito menos interativa e visual. Ou, realmente, não é interativo. Era difícil entrar nos programas e, portanto, os programadores precisavam que os compiladores fossem muito inteligentes quanto à análise de sintaxe e recuperação de erros.

Naquela época passada, o ciclo de edição, compilação e execução poderia envolver a preparação de cartões perfurados com um perfurador de cartões e, em seguida, alinhando-se a uma janela de envio de trabalho em que um funcionário pegava os cartões perfurados e os submetia à máquina. Mais tarde, o programador coletaria a saída (impressa em papel) de outra janela. Se o programa apresentasse erros, a saída consistiria apenas no diagnóstico do compilador. Quando os tempos de resposta são longos, o custo adicional de digitação end ifem vez de apenas )se justifica se ele ajuda a melhorar a qualidade do diagnóstico, porque as necessidades programador para corrigir tantos erros quanto possível em uma única iteração para reduzir o número de perdas de tempo iterações através da janela de envio de tarefas.

Quando falta uma chave de fechamento, é difícil dizer qual chave é a que não está fechada. (O compilador pode ter que analisar o recuo para fazer uma estimativa.) Se você excluir uma chave de fechamento dentro de uma função, parece que o restante do arquivo faz parte dessa função, resultando em uma enxurrada de mensagens de erro inúteis. Considerando que, se você tiver uma end functionsintaxe, o compilador poderá deduzir onde a função incorreta termina, recuperar e analisar as funções subseqüentes corretamente, fornecendo diagnósticos adicionais, se houver, que sejam significativos.

Quando você trabalha no editor de texto com reconhecimento de código, que recua e colore automaticamente o código, em uma tela de alta resolução na qual é possível ver sessenta ou mais linhas, os argumentos para esses tipos de idiomas desajeitados não se aplicam mais. Você pode editar e reconstruir incrementalmente os programas tão rapidamente que pode lidar com um erro de cada vez. Além disso, vendo grandes seções do programa simultaneamente na tela e mantendo o recuo adequado, você pode reduzir a ocorrência desses tipos de erros de aninhamento em primeiro lugar. E um bom editor de texto de programação sinaliza alguns tipos de erros de sintaxe enquanto você digita. Além disso, existem editores dobráveis ​​que recolherão os blocos de um programa com base em sua sintaxe, fornecendo uma visão "estrutura de tópicos" de sua estrutura.

O Lisp usou parênteses desde o início e, talvez, não por acaso, os hackers do Lisp foram pioneiros na programação como uma experiência interativa, construindo sistemas que aceitavam programas em pequenos pedaços (expressões).

Na verdade, você não precisa de símbolos finais, como ilustra a linguagem Python. A identificação pode ser apenas a estrutura. Os seres humanos já usam recuo para grok a estrutura do código, mesmo em idiomas onde a máquina depende de símbolos ou frases finais.

Kaz
fonte
-1

Se você estiver usando um IDE, basta pressionar Crtl+ k+ De o IDE fará o resto do trabalho.

Jagz W
fonte
que IDE usos eclipse Ctrl-Shift-I para recuo e Ctrl-Shift-F para formatar
catraca aberração
check-in visual studio
Jagz W 8/12