Alguém ainda usa [goto] em C # e se sim, por quê? [fechadas]

104

Eu queria saber se alguém ainda usa a sintaxe de palavra-chave "goto" em C # e quais são as possíveis razões para isso.

Eu tendo a ver quaisquer declarações que façam o leitor pular o código como má prática, mas me pergunto se há algum cenário confiável para o uso de tal sintaxe.

Ir para definição de palavra-chave

Brian Scott
fonte
3
o que quer dizer "ainda"? Houve um período em que as pessoas o usaram o tempo todo [em c #]?
Maciço de
4
@Massif: "still" pretendia enfatizar a opinião moderna do uso de "goto" como um prelúdio para o código espaguete e uma falta de legibilidade no código-fonte. Muito raramente você vê qualquer exemplo de código incluindo essa palavra-chave em particular, por isso eu estava interessado em perguntar em primeiro lugar.
Brian Scott
50
Se o leitor "pular de um lado para outro" o código é uma prática ruim, você também evita "quebrar", "continuar", "lançar" e "retornar"? Todos eles causam uma ramificação no fluxo de controle, às vezes uma ramificação não local. "Jogue" nem mesmo diz para onde está indo, ao contrário de ir para.
Eric Lippert
2
Eu costumo gotoquebrar um loop e voltar ao início da declaração de acordo com a condição específica
Nitin Sawant,
3
Eu gosto de ir para. Tentei evitá-lo devido à tendência de pessoas dizerem para evitá-lo porque torna o código mais difícil de ler. Tendo aprendido a linguagem Assembly e as instruções de ramificação, acho que às vezes isso pode tornar o código mais legível. Eu realmente acho que vários usos em um método e ir muito longe no código pode fazer mais mal do que bem. Mas se você está pensando que um goto funcionaria bem aqui, um goto simples de vez em quando não deve fazer com que você saia do seu caminho para evitar, apenas porque o consenso comum é evitá-lo.
eaglei22

Respostas:

93

Existem alguns casos (raros) em que goto pode realmente melhorar a legibilidade. Na verdade, a documentação à qual você vinculou lista dois exemplos:

Um uso comum de goto é transferir o controle para um rótulo de switch-case específico ou o rótulo padrão em uma instrução switch.

A instrução goto também é útil para sair de loops profundamente aninhados.

Aqui está um exemplo para o último:

for (...) {
    for (...) {
        ...
        if (something)
            goto end_of_loop;
    }
}

end_of_loop:

Obviamente, também existem outras maneiras de contornar esse problema, como refatorar o código em uma função, usar um bloco fictício em torno dele, etc. (consulte esta questão para obter detalhes). Como uma observação lateral, os designers da linguagem Java decidiram banir goto completamente e introduzir uma instrução break rotulada .

Heinzi
fonte
47
Normalmente eu tentaria refatorar isso para colocar os loops em um método separado do qual poderia simplesmente retornar ...
Jon Skeet
2
@Heinzi - Não vi goto sendo justificado. Como Jon diz, se estiver "garantido", o código está implorando para ser refatorado.
manojlds
29
rotulado de break, apenas uma maneira mais longa de dizer goto porque faz a mesma coisa ...
Jesus Ramos
20
@ Jesus, mas com goto, você pode ir a qualquer lugar. A quebra marcada garante que você está saindo do loop.
mihsathe
1
A menos que você esteja sendo aventureiro e usando goto com um endereço (eu já vi isso antes), esse problema está atenuado. E duvido que alguém esteja usando ganchos de desvio em seu código C # e Java para explorar as instruções goto.
Jesus Ramos
66

Eu me lembro dessa parte

switch (a)     
{ 
    case 3: 
        b = 7;
        // We want to drop through into case 4, but C# doesn't let us
    case 4: 
        c = 3;
        break; 
    default: 
        b = 2;
        c = 4;
        break; 
}

Para algo assim

switch (a)     
{
    case 3: 
        b = 7;
        goto case 4;    
    case 4: 
        c = 3;
        break;     
    default: 
        b = 2;
        c = 4;
        break;
}

Consulte isto

V4Vendetta
fonte
17
Na verdade, eu vejo isso como o motivo mais válido para usar [goto] ainda. Pelo menos neste cenário, aumenta a legibilidade para os programadores que não sabem que os casos entram uns nos outros sem uma instrução break.
Brian Scott
11
@Brian Scott, V4Vendetta. A menos que eu esteja enganado, a primeira instrução não compila em C #. Isso ajudaria na compreensão do programador.
Jodrell
2
V4Vendetta, por que você inseriu uma quebra no primeiro trecho de código ...? Era melhor mostrar sem interrupção, caso contrário, os dois fragmentos fazem coisas diferentes. A razão pela qual você precisa do goto no segundo exemplo é precisamente porque o primeiro não compila em C # (como faria em C).
Stephen Holt de
23

Eu o uso extensivamente no Eduasync para mostrar o tipo de código que o compilador gera para você ao usar métodos assíncronos em C # 5. Você veria a mesma coisa nos blocos iteradores.

No entanto, em código "normal", não me lembro da última vez que o usei ...

Jon Skeet
fonte
1
você pode fornecer um pequeno exemplo de por que essa abordagem foi preferida ou foi simplesmente uma preferência pessoal?
Brian Scott
1
@Brian: Não está muito claro o que você quer dizer. Eduasync mostra o código C # equivalente ao que o compilador faz por você - e gera código que usa goto, efetivamente ...
Jon Skeet
9

goto é ótimo para quebrar muitos loops onde break não funcionaria bem (digamos em condições de erro), e como Kragen disse, goto é usado pelo compilador para gerar instruções switch e algumas outras coisas também.

Jesus Ramos
fonte
7
Certamente, "interromper" / "continuar" são as melhores abordagens para o gerenciamento de loop, em vez de exigir que o editor de código salte o código-fonte tentando entender onde ocorre a próxima etapa.
Brian Scott
6
Não se você tiver loops aninhados.
Jesus Ramos
1
ok, posso ver isso como um cenário válido.
Brian Scott
1
Em uma condição de erro, você deve considerar lançar uma exceção.
Jodrell
6
Se você quiser tratar o erro internamente sem exceção, esta seria uma maneira válida de fazer isso.
Jesus Ramos
8

Não me lembro de nunca ter usado goto. Mas talvez melhore a intenção de um loop eterno do qual você realmente nunca deseja sair (não break, mas você ainda pode returnou throw):

forever: {
  // ...
  goto forever;
}

Então, novamente, um simples while (true) deve bastar ...

Além disso, você pode usar em uma situação em que deseja que a primeira iteração de um loop comece no meio do loop: veja aqui um exemplo.

Jordão
fonte
A resposta vinculada contém um uso "interessante" de goto.. e while(true) {..}não é um uso interessante ..
user2864740
5

O compilador usa gotoinstruções em várias partes do código gerado, por exemplo, em tipos de blocos iteradores gerados (gerados ao usar a yield returnpalavra-chave - tenho certeza de que os tipos de serialização XML gerados também têm algumas gotoinstruções em algum lugar também.

Consulte os detalhes de implementação do bloco Iterator: máquinas de estado geradas automaticamente para obter mais detalhes sobre por que / como o compilador C # lida com isso.

Além do código gerado, não há um bom motivo para usar uma gotoinstrução no código normal - torna o código mais difícil de entender e, como resultado, mais sujeito a erros. Por outro lado, usandogoto instruções em código gerado como este pode simplificar o processo de geração e normalmente é bom porque ninguém vai ler (ou modificar) o código gerado e não há chance de erros serem cometidos porque uma máquina está fazendo a escrita.

Veja a declaração Go-to considerada prejudicial para um argumento contra goto, bem como um pedaço clássico da história da programação.

Justin
fonte
4
Isso é estúpido: há vários casos em que goto é útil, conforme ilustrado por outras respostas. Ou você os esfrega explicitamente ou apenas dizer "está errado" é, bem, errado.
o0 '.
@Lohoris Não estou comprando - cada exemplo que vi em que goto "melhora a legibilidade" (incluindo as respostas aqui) seria muito mais legível após uma simples refatoração.
Justin
3
@ Justin não, às vezes os loops aninhados são apenas a maneira mais natural de fazer algo, por exemplo, se você estiver percorrendo um array de arrays.
o0 '.
6
@ Justin nem sempre é mais claro colocá-lo em uma função. Você está forçando algo (tendo uma função) apenas para evitar algo que odeia religiosamente (usando um goto). Indicação clara de que está fazendo algo errado.
o0 '.
3
As chamadas do @Justin Functions têm sobrecarga. gotonão. Algo a considerar.
Dan Bechard
2

O processador implementa pelo menos uma instrução de salto e tenho certeza que muitas instruções as usam em sua implementação ou interpretação.

Uma das coisas boas em usar um idioma de ou geração é que esses detalhes físicos são abstraídos de nós. Embora devamos estar atentos à lei da abstração com vazamento , acho que também devemos usar nossas ferramentas da maneira pretendida ( desculpe ). Se eu estivesse escrevendo código e umgoto parecesse uma boa ideia, seria hora de refatorar. O objetivo de uma linguagem estruturada é evitar esses "saltos" e criar um fluxo lógico em nossa engenharia.

Devo evitar o uso de, breakmas não posso ignorar o benefício de desempenho. No entanto, se eu tiver loops aninhados que precisam ser mutuamente break, é hora de refatorar.

Se alguém pode propor o uso de goto que pareça melhor do que refatorar, retirarei minha resposta de bom grado.

Espero não ser culpado de correr para o " galpão de bicicletas " aqui. Como Kragen diz, o que é bom para Dijkstra é bom o suficiente para mim.

Jodrell
fonte
1
Pegando um dynamicobjeto e percorrendo seu gráfico de objeto que contém vários dicionários para chegar aos valores que preciso. Não faz sentido usar métodos que têm um parâmetro de dynamicainda esperar uma forma de objeto exata. Com goto para quebrar várias camadas e continuar caminhando por uma coleção desses objetos. [Eu não possuo os tipos, então não posso fornecer melhor acesso, então é reflexo ou dinâmico]
Chris Marisic
-6

Goto nunca é melhor. E continuar, interromper (exceto em interruptor / caso), retorno (múltiplo) e lançamento também devem ser mantidos ao mínimo. Você nunca quer escapar do meio de loops de ninho. Você sempre deseja que as instruções de controle do loop tenham todo o controle do loop. O recuo contém informações, e todas essas afirmações jogam essas informações fora. Você também pode remover todo o recuo.

Kirk Augustin
fonte
10
Você gostaria de sair de um loop se não houver motivo para manter a execução do loop. Caso contrário, você acabará perdendo mais tempo de processamento em loops mais longos sem motivo.
Skuld
12
@Kirk, isso soa mais como uma opinião do que como algo quantitativo?
Brian Scott