Dlaczego pętla for zachowuje się inaczej podczas migracji kodu VB.NET do C #?

87

Jestem w trakcie migracji projektu z Visual Basic do C # i musiałem zmienić sposób fordeklarowania używanej pętli.

W VB.NET forpętla jest zadeklarowana poniżej:

Dim stringValue As String = "42"

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

Które wyjścia:

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

W C # forpętla jest zadeklarowana poniżej:

string stringValue = "42";

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

A wynik:

42 1
42 1 2
42 1 2 3

To oczywiście nie jest poprawne, więc musiałem nieznacznie zmienić kod i dołączyć zmienną całkowitą, która utrzymywałaby długość ciągu.

Zobacz poniższy kod:

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

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

A wynik:

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

Teraz moje pytanie dotyczy tego, jak Visual Basic różni się od C # pod względem Visual Basic przy użyciu stringValue.Lengthwarunku w forpętli, mimo że za każdym razem, gdy występuje pętla, zmienia się długość ciągu. Natomiast w C #, jeśli używam warunku stringValue.Lengthw forpętli, zmienia on początkową wartość ciągu za każdym razem, gdy występuje pętla. Dlaczego to?

slee423
źródło
7
Microsoft jasno przedstawia twój problem ...
Codexer
39
@ Çöđěxěŕ: Przestań, proszę. Gdyby pomocne było automatyczne usuwanie tagów z tytułu bez względu na kontekst, witryna by to zrobiła.
Ry-
11
@ Çöđěxěŕ Możesz znaleźć trochę informacji na ten temat tutaj
pushkin
35
@ Çöđěxěŕ Nie sądzę, że te dwa posty są ze sobą sprzeczne. Chodzi o to: nie przyklejaj niezręcznie w tytule tagu typu „pytanie o tę rzecz - tag”. Ale jeśli tytuł jest pełnym zdaniem, które zawiera tag, czasami jest w porządku. „Dlaczego pętla for zachowuje się inaczej podczas migracji” wydaje się zbyt ogólnikowym tytułem.
puszkin
26
@ Çöđěxěŕ „migracja kodu VB.NET do C #” to wyraźnie pełna fraza, która dodaje przydatne informacje do tytułu. Nie wprowadzaj zmian, które zmniejszają przejrzystość. Miejmy nadzieję, że ten pogląd jest na tyle zdrowy, że nie potrzebujemy postu w Meta, aby go poprzeć.
jpmc26

Odpowiedzi:

115

W języku C # warunek brzegowy pętli jest oceniany przy każdej iteracji. W VB.NET jest oceniany tylko przy wejściu do pętli.

Tak więc w wersji C # w pytaniu, ponieważ długość stringValuejest zmieniana w pętli, ostateczna wartość zmiennej pętli zostanie zmieniona.

W VB.NET ostateczny warunek jest włącznie, więc <=zamiast <w C # należy go używać .

Ocena warunku końcowego w C # ma taki wniosek, że nawet jeśli nie zmienia się, ale jest kosztowna do obliczenia, to powinna zostać obliczona tylko raz przed pętlą.

Andrew Morton
źródło
Dziękuję za odpowiedź. Teraz zdaję sobie sprawę, że używanie <=pozwala mi na iterację i daje takie same wyniki, jak kod VB. Jednak bardziej interesuje mnie, dlaczego musiałem zadeklarować zmienną całkowitą, a VBnie musiałem tego robić. Zamierzam zaktualizować moje pytanie, aby wyświetlało ten sam wynik.
slee423
@ slee423 Powód jest podany w pierwszym zdaniu mojej odpowiedzi. Ponieważ stringValuew pętli zmienia się długość , ostateczna wartość zmiennej pętli zostanie zmieniona.
Andrew Morton,
1
przepraszam, dziękuję za odpowiedź. Dziękuję za bardziej szczegółowe wyjaśnienie.
slee423
1
@ slee423 Dodałem to do odpowiedzi, ponieważ rzeczywiście to wyjaśnia.
Andrew Morton
22

Teraz moje pytanie dotyczy tego, w jaki sposób VB różni się od C # pod względem VB przy użyciu warunku stringValue.Length w pętli for, mimo że za każdym razem, gdy występuje pętla, zmienia się długość ciągu.

Zgodnie z dokumentacją VB.NET :

Jeśli zmienisz wartość counterwhile wewnątrz pętli, kod może być trudniejszy do odczytania i debugowania. Zmiana wartości start, endlub stepnie ma wpływu na wartość iteracji, które zostały ustalone, gdy pętla została po raz pierwszy wprowadzona.

Tak więc wartość To 10 - stringValue.Lengthjest obliczana raz i ponownie używana do zakończenia pętli.

Jednak spójrz na C # dla instrukcji

Jeśli for_conditionnie istnieje lub jeśli wynik oceny daje true, kontrola jest przenoszona do instrukcji osadzonej. Kiedy i jeśli sterowanie osiągnie punkt końcowy instrukcji osadzonej (prawdopodobnie z wykonania instrukcji continue), wyrażenia for_iterator, jeśli istnieją, są oceniane w kolejności, a następnie wykonywana jest kolejna iteracja, zaczynając od oceny instrukcji for_conditionw kroku powyżej.

Co w zasadzie oznacza, że ​​warunek ; i <= 10 - stringValueLength;jest oceniany ponownie za każdym razem.

Tak więc, jak widziałeś, jeśli chcesz zreplikować kod, musisz zadeklarować końcowy licznik w języku c # przed uruchomieniem pętli.

Camilo Terevinto
źródło
11

Aby uczynić przykład bardziej zrozumiałym, przekonwertuję obie pętle for na pętle while w języku 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++;
}

DO#

string stringValue = "42";

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

Różnica jest zatem taka:

VB.NET buforuje maksymalną wartość dla i, ale C # przelicza ją za każdym razem.

Maxime Recuerda
źródło
2
Nie musisz konwertować pętli na while
Panagiotis Kanavos
10
Pomogło mi to zrozumieć for loopsna początku, myślę, że są one dużo bardziej zrozumiałe. Dlatego „przetłumaczyłem” przykłady w języku while loops, aby ułatwić zrozumienie.
Maxime Recuerda
7

Ponieważ forin VB jest inną semantyczną niż forin C # (lub jakikolwiek inny język podobny do C)

W języku VB forinstrukcja zwiększa wartość licznika z jednej wartości do drugiej.

W językach C, C ++, C # itp. forInstrukcja po prostu ocenia trzy wyrażenia:

  • Pierwsze wyrażenie to zwykle inicjalizacja
  • Drugie wyrażenie jest oceniane na początku każdej iteracji, aby określić, czy warunek końcowy został spełniony
  • Trzecie wyrażenie jest oceniane na końcu każdej iteracji, które zwykle jest jednostką zwiększającą.

W VB musisz podać zmienną numeryczną, którą można przetestować względem wartości końcowej i zwiększać przy każdej iteracji

W językach C, C ++, C # itd. Te trzy wyrażenia są ograniczone w minimalnym stopniu; wyrażenie warunkowe musi mieć wartość prawda / fałsz (lub liczbę całkowitą zero / niezerową w C, C ++). Nie musisz w ogóle wykonywać inicjalizacji, możesz iterować dowolny typ w dowolnym zakresie wartości, iterować wskaźnik lub odwołanie po złożonej strukturze lub w ogóle nie wykonywać iteracji.

Tak więc w C # itd. Wyrażenie warunku musi być w pełni ocenione w każdej iteracji, ale w VB wartość końcowa iteratora musi zostać oszacowana na początku i nie musi być oceniana ponownie.

C Robinson
źródło