Por que um loop for se comporta de maneira diferente ao migrar o código VB.NET para C #?

87

Estou no processo de migração de um projeto do Visual Basic para C # e tive que alterar a forma como um forloop em uso é declarado.

Em VB.NET, o forloop é declarado abaixo:

Dim stringValue As String = "42"

For i As Integer = 1 To 10 - stringValue.Length
   stringValue = stringValue & " " & CStr(i)
   Console.WriteLine(stringValue)
Next

Quais saídas:

42 1
42 1 2
42 1 2 3
42 1 2 3 4
42 1 2 3 4 5
42 1 2 3 4 5 6
42 1 2 3 4 5 6 7
42 1 2 3 4 5 6 7 8

Em C #, o forloop é declarado abaixo:

string stringValue = "42";

for (int i = 1; i <= 10 - stringValue.Length; i ++)
{
   stringValue = stringValue + " " + i.ToString();
   Console.WriteLine(stringValue);
}

E a saída:

42 1
42 1 2
42 1 2 3

Isso obviamente não está correto, então eu tive que mudar o código levemente e incluí uma variável inteira que manteria o comprimento da string.

Por favor veja o código abaixo:

string stringValue = "42";
int stringValueLength = stringValue.Length;

for (int i = 1; i <= 10 - stringValueLength; i ++)
{
   stringValue = stringValue + " " + i.ToString();
   Console.WriteLine(stringValue);
}

E a saída:

42 1
42 1 2
42 1 2 3
42 1 2 3 4
42 1 2 3 4 5
42 1 2 3 4 5 6
42 1 2 3 4 5 6 7
42 1 2 3 4 5 6 7 8

Agora minha pergunta resolve em torno de como o Visual Basic difere do C # em termos de Visual Basic usando a stringValue.Lengthcondição no forloop, embora cada vez que o loop ocorra, o comprimento da string seja alterado. Enquanto em C #, se eu usar a condição stringValue.Lengthin the forloop, ele altera o valor inicial da string cada vez que ocorre o loop. Por que é isso?

slee423
fonte
7
A Microsoft descreve claramente qual é o seu problema ...
Çöđěxěŕ
39
@ Çöđěxěŕ: Pare, por favor. Se a remoção automática de tags do título sem levar em conta o contexto fosse útil, o site faria isso.
Ry-
11
@ Çöđěxěŕ Você pode encontrar mais informações sobre isso aqui
pushkin
35
@ Çöđěxěŕ Não creio que os dois posts se contradigam. O que quero dizer é: não coloque uma tag no título de maneira desajeitada como "pergunta sobre isso - tag". Mas se o título for uma frase completa que inclui a tag, às vezes não tem problema . "Por que o loop for se comporta de maneira diferente durante a migração" parece um título genérico demais.
pushkin de
26
@ Çöđěxěŕ "migrar código VB.NET para C #" é claramente uma frase completa que adiciona informações úteis ao título. Não faça edições que diminuam a clareza. Felizmente, essa noção é de bom senso o suficiente para que não precisemos de um post Meta para fazer o backup.
jpmc26

Respostas:

115

Em C #, a condição de limite do loop é avaliada em cada iteração. No VB.NET, ele é avaliado apenas na entrada no loop.

Portanto, na versão C # em questão, como o comprimento de stringValueestá sendo alterado no loop, o valor final da variável do loop será alterado.

No VB.NET, a condição final é inclusiva, então você usaria em <=vez de <no C #.

A avaliação da condição final em C # tem o corolário de que, mesmo que não varie, mas seja caro de calcular, ela deve ser calculada apenas uma vez antes do loop.

Andrew Morton
fonte
Obrigado pela resposta. Agora percebo que o uso <=me permite iterar e ter a mesma saída que o código VB. No entanto, estou mais interessado em saber por que tive que declarar a variável inteira e por VBque não. Vou atualizar minha pergunta para mostrar o mesmo resultado.
slee423,
@ slee423 O motivo é dado na primeira frase da minha resposta. Como o comprimento de stringValueestá sendo alterado no loop, o valor final da variável do loop será alterado.
Andrew Morton,
1
desculpas, obrigado por essa resposta. E obrigado por elaborá-lo com mais detalhes para mim.
slee423,
1
@ slee423 Acrescentei isso à resposta, pois de fato a esclarece.
Andrew Morton,
22

Agora, minha pergunta resolve em torno de como o VB difere do C # em termos de VB usando a condição stringValue.Length no loop for, embora cada vez que o loop ocorra, o comprimento da string mude.

De acordo com o VB.NET documentação :

Se você alterar o valor de counterwhile dentro de um loop, seu código pode ser mais difícil de ler e depurar. Mudar o valor start, endoustep não afeta os valores de iteração que foram determinados quando o loop foi inserido pela primeira vez.

Então, o valor de To 10 - stringValue.Length é avaliado uma vez e reutilizado até a saída dos loops.

No entanto, observe o c # para obter a declaração

Se o for_conditionnão estiver presente ou se a avaliação resultar true, o controle é transferido para a demonstração embutida. Quando e se o controle atinge o ponto final da instrução incorporada (possivelmente a partir da execução de uma instrução continue), as expressões de for_iterator, se houver, são avaliadas em sequência e, em seguida, outra iteração é realizada, começando com a avaliação de for_conditionna etapa acima.

O que basicamente significa que a condição ; i <= 10 - stringValueLength; é avaliada novamente a cada vez.

Portanto, como você viu, se quiser replicar o código, você precisará declarar o contador final em c # antes de iniciar o loop.

Camilo Terevinto
fonte
11

Para tornar o exemplo mais compreensível, converterei ambos os loops for em loops while C # .

VB.NET

string stringValue = "42";

int min = 1;
int max = 10 - stringValue.Length;
int i = min;
while (i <= max)
{
    stringValue = stringValue + " " + stringValue.Length.ToString();
    Console.WriteLine(stringValue);
    i++;
}

C #

string stringValue = "42";

int i = 1;
while (i <= 10 - stringValue.Length)
{
    stringValue = stringValue + " " + stringValue.Length.ToString();
    Console.WriteLine(stringValue);
    i++;
}

A diferença é então:

O VB.NET armazena em cache o valor máximo de i, mas C # o recalcula todas as vezes.

Maxime Recuerda
fonte
2
Você não precisa converter loops para while
Panagiotis Kanavos,
10
Ajudou-me a compreender os primeiros for loops, acho que são muito mais compreensíveis. É por isso que eu 'traduzi' os exemplos while loopspara ajudar a compreensão.
Maxime Recuerda
7

Porque o forem VB é uma semântica diferente do que o forem C # (ou qualquer outra linguagem semelhante a C)

No VB, a forinstrução está especificamente incrementando um contador de um valor para outro.

Em C, C ++, C #, etc., a forinstrução simplesmente avalia três expressões:

  • A primeira expressão é normalmente uma inicialização
  • A segunda expressão é avaliada no início de cada iteração para determinar se a condição terminal foi atendida
  • A terceira expressão é avaliada no final de cada iteração, que normalmente é um incrementador.

Em VB, você deve fornecer uma variável numérica que pode ser testada contra um valor terminal e incrementada a cada iteração

Em C, C ++, C #, etc., as três expressões são minimamente restritas; a expressão condicional deve ser avaliada como verdadeiro / falso (ou inteiro zero / diferente de zero em C, C ++). Você não precisa realizar nenhuma inicialização, você pode iterar qualquer tipo em qualquer intervalo de valores, iterar um ponteiro ou referência sobre uma estrutura complexa ou não iterar nada.

Portanto, em C #, etc., a expressão de condição deve ser totalmente avaliada em cada iteração, mas em VB, o valor terminal do iterador deve ser avaliado no início e não precisa ser avaliado novamente.

C Robinson
fonte