Por que o C # permite {} blocos de código sem uma instrução anterior?

86

Por que C # permitem que blocos de código sem instrução anterior (por exemplo if, else, for, while)?

void Main()
{
    {   // any sense in this?
        Console.Write("foo");
    }
}
Seldon
fonte
70
Existe alguma razão para que não deva ?
Jamiec
30
Mas como as respostas mostram que tem mais significado do que apenas "não dói, então vamos permitir". Este é um ponto de fazer tais perguntas.
Seldon
8
+1 pergunta parece inocente, mas as respostas realmente me ensinaram algo valioso
Nicolas78
7
@Akash: Sem perguntar por quê, nunca iremos melhorar.
richard
7
@Akash: Isso parece uma atitude elitista arrojada. As pessoas sempre têm dúvidas e, se não houver razão para perguntar por quê, então não há sentido em ter SO. Este site não é tanto para ter pessoas resolvendo nossos problemas imediatos (embora isso aconteça), mas para fornecer um repositório de perguntas e respostas que nos ajudarão a ser melhores programadores. ASSIM é para perguntar POR QUE! :-)
richard

Respostas:

90

No contexto que você dá, não há significado. Escrever uma string constante no console funcionará da mesma maneira em qualquer lugar no fluxo do programa. 1

Em vez disso, você normalmente os usa para restringir o escopo de algumas variáveis ​​locais. Isso é mais elaborado aqui e aqui . Olhe para a resposta de João Angelo e a resposta de Chris Wallis para pequenos exemplos. Eu acredito que o mesmo se aplica a algumas outras linguagens com sintaxe de estilo C também, embora não sejam relevantes para esta questão.


1 A menos, é claro, que você decida tentar ser engraçado e criar sua própria Consoleaula, com um Write()método que faz algo totalmente inesperado.

BoltClock
fonte
24
Re: outras línguas: geralmente, mas nem sempre. JavaScript é uma exceção notável; declarar uma variável local em um bloco particular não faz o escopo da variável para o bloco.
Eric Lippert
@EricLippert: Em C #, declarar uma variável dentro de um bloco faz com que o escopo da variável seja feito da perspectiva do tempo de execução? Pelo que posso dizer, os tempos de vida das variáveis ​​muitas vezes não são limitados por blocos de escopo, um fato que é observável em projetos de linguagem mista se a primeira coisa feita com uma variável em um loop é passá-la como um outparâmetro para uma implementação de interface escrita em outro idioma, e essa implementação lê a variável antes de gravá-la.
supercat
E em C ++, por causa de RAII em vez de GC, é muito mais útil.
Deduplicator
Em C ++, ele atua como um bloco try / catch, portanto, as variáveis ​​e classes declaradas dentro desse escopo saltam da pilha quando você sai desse escopo. Isso ajuda a evitar colisões de nomes e reduz o consumo de memória da função. Ele também remove essas variáveis ​​do preenchimento automático do editor de código. Freqüentemente, considero os blocos try / catch mais úteis.
Kit10
144

O { ... }tem pelo menos o efeito colateral de introduzir um novo escopo para variáveis ​​locais.

Eu tendo a usá-los em switchdeclarações para fornecer um escopo diferente para cada caso e, dessa forma, permitir-me definir uma variável local com o mesmo nome no local mais próximo possível de seu uso e também denotar que eles são válidos apenas no nível do caso.

Joao angelo
fonte
6
Se você precisa de osciloscópios em seus casos, eles são muito grandes! Método de extração.
Jay Bazuzi
20
@Jay Bazuzi, só porque quero ter uma variável local com o mesmo nome em mais de um caso não significa que elas tenham que ser enormes. Você está apenas saltar para grandes conclusões ... :)
João Angelo
Parabéns pelo seu emblema da Grande Resposta! :)
BoltClock
1
@BoltClock, obrigado, só preciso de mais algumas perguntas sobre {}como manter os emblemas de ouro chegando ... :)
João Angelo
56

Não é tanto um recurso do C # quanto um efeito colateral lógico de muitas linguagens de sintaxe C que usam colchetes para definir o escopo .

Em seu exemplo, as chaves não têm efeito algum, mas no código a seguir elas definem o escopo e, portanto, a visibilidade de uma variável:

Isso é permitido porque i sai do escopo no primeiro bloco e é definido novamente no próximo:

{
    {
        int i = 0;
    }

    {
        int i = 0;
    }
}

Isso não é permitido porque i saiu do escopo e não é mais visível no escopo externo:

{
    {
        int i = 0;
    }

    i = 1;
}

E assim por diante.

Chris Wallis
fonte
1
Eu li sobre as disparidades na nomenclatura dos colchetes em vários sabores do inglês, mas em qual sabor são {}conhecidos como parênteses?
BoltClock
Boa pergunta! Acho que um mentor meu plantou em meu cérebro há uma década. Eu provavelmente deveria chamá-los de 'colchetes' ou 'colchetes'. Cada um com seus próprios ... en.wikipedia.org/wiki/…
Chris Wallis
10
No meu vocabulário '(' = parênteses, '{' = colchetes, '[' = colchetes
pb.
Espere aí, eu os chamaria de chave, e a Wikipedia inclui essa nomenclatura. O Oxford English Dictionary define o uso de colchetes como: One of two marks of the form [ ] or ( ), and in mathematical use also {}, used for enclosing a word or number of words, a portion of a mathematical formula, or the like, so as to separate it from the context; Em qualquer caso, eles não são parênteses, mas 'colchetes' parece OK.
dumbledad
IME, "colchetes" e "colchetes".
cp.engr
18

Considero {}como uma declaração que pode conter várias declarações.

Considere uma instrução if que exista de uma expressão booleana seguida por uma instrução. Isso funcionaria:

if (true) Console.Write("FooBar");

Isso também funcionaria:

if (true)
{
  Console.Write("Foo");
  Console.Write("Bar");
}

Se não estou enganado, isso é chamado de instrução em bloco.

Uma vez que {}pode conter outras instruções, também pode conter outras {}. O escopo de uma variável é definido por seu pai {}(instrução de bloco).

O que estou tentando mostrar é que {}é apenas uma declaração, então não requer um se ou qualquer coisa ...

RBaarda
fonte
+1 Isso é exatamente o que os colchetes são nas linguagens de estilo C - a chamada "instrução composta".
Michael Ekstrand
12

A regra geral em linguagens de sintaxe C é "qualquer coisa no meio { }deve ser tratada como uma única instrução, e pode ir a qualquer lugar que uma única instrução":

  • Depois de um if.
  • Depois de um for, whileou do.
  • Em qualquer lugar do código .

Para todos os efeitos, é como a gramática da linguagem incluiu isto:

     <statement> :== <definition of valid statement> | "{" <statement-list> "}"
<statement-list> :== <statement> | <statement-list> <statement>

Ou seja, "uma declaração pode ser composta de (várias coisas) ou de uma chave de abertura, seguida por uma lista de declarações (que pode incluir uma ou mais declarações), seguida de uma chave fechada". IE "um { }bloco pode substituir qualquer instrução, em qualquer lugar". Incluindo no meio do código.

Não permitir um { }bloco em qualquer lugar que uma única instrução possa ir tornaria a definição da linguagem mais complexa .

Massimo
fonte
Alguém acabou de aumentar essa resposta (após 9 anos), que não era realmente uma resposta específica para essa pergunta, mas muito mais uma discussão genérica. Pode muito bem ter sido substituído pela linguagem e pelas bibliotecas .. Mas foi um prazer de qualquer maneira, obrigado.
Massimo
1

Porque C ++ (e java) permitia blocos de código sem uma instrução anterior.

C ++ permitiu isso porque C permitiu.

Você poderia dizer que tudo se resume ao fato de que o design de linguagem de programa dos EUA (baseado em C) venceu, em vez do design de linguagem de programa europeu ( baseado em Modula-2 ).

(As instruções de controle atuam em uma única instrução, as instruções podem ser grupos para criar novas instruções)

Ian Ringrose
fonte
Você quer dizer Módulo2 ou Modula2?
Richard Ev
Na verdade, esta é a resposta correta. Além disso, eu apenas responderia "Porque todas as linguagens de computador fazem."
Fattie
1
// if (a == b)
// if (a != b)
{
    // do something
}
user202448
fonte
0

1 Porque ... É Manter a Área de Escopo da instrução .. ou Função, Isso é realmente útil para mane o código grande ..

{
    {
        // Here this 'i' is we can use for this scope only and out side of this scope we can't get this 'i' variable.

        int i = 0;
    }

    {
        int i = 0;
    }
}

insira a descrição da imagem aqui

Raj
fonte
0

Você perguntou "por que" C # permite blocos de código sem instruções anteriores. A pergunta "por que" também poderia ser interpretada como "quais seriam os possíveis benefícios desse construto?"

Pessoalmente, eu uso blocos de código sem instrução em C #, onde a legibilidade é muito melhorada para outros desenvolvedores, embora tenha em mente que o bloco de código limita o escopo das variáveis ​​locais. Por exemplo, considere o seguinte snippet de código, que é muito mais fácil de ler graças aos blocos de código adicionais:

OrgUnit world = new OrgUnit() { Name = "World" };
{
    OrgUnit europe = new OrgUnit() { Name = "Europe" };
    world.SubUnits.Add(europe);
    {
        OrgUnit germany = new OrgUnit() { Name = "Germany" };
        europe.SubUnits.Add(germany);

        //...etc.
    }
}
//...commit structure to DB here

Estou ciente de que isso poderia ser resolvido de forma mais elegante, usando métodos para cada nível de estrutura. Mas, novamente, tenha em mente que coisas como semeadores de dados de amostra geralmente precisam ser rápidos.

Portanto, mesmo que o código acima seja executado linearmente, a estrutura do código representa a estrutura do "mundo real" dos objetos, tornando mais fácil para outros desenvolvedores entender, manter e estender.

Marco
fonte