Cofnąłbym się tutaj. Koncentrujesz się na wybrednych szczegółach kodu, ale brakuje większego obrazu. Rzućmy okiem na jedną z twoich przykładowych pętli:
int offset = 0;
while(true)
{
Record r = Read(offset);
if(r == null)
{
break;
}
// do work
offset++;
}
Jakie jest znaczenie tego kodu? Oznacza to: „popracuj nad każdym rekordem w pliku”. Ale nie tak wygląda kod . Kod wygląda jak „zachowaj przesunięcie. Otwórz plik. Wejdź w pętlę bez warunku zakończenia. Przeczytaj rekord. Testuj pod kątem nieważności”. Wszystko to zanim przejdziemy do pracy! Pytanie, które powinieneś zadać, brzmi: „w jaki sposób mogę dopasować wygląd tego kodu do semantyki? ” Ten kod powinien być:
foreach(Record record in RecordsFromFile())
DoWork(record);
Teraz kod brzmi jak jego intencja. Oddziel swoje mechanizmy od semantyki . W swoim oryginalnym kodzie łączysz mechanizm - szczegóły pętli - z semantyką - pracą wykonaną dla każdego rekordu.
Teraz musimy wdrożyć RecordsFromFile(). Jaki jest najlepszy sposób na wdrożenie tego? Kogo to obchodzi? To nie jest kod, na który ktoś będzie patrzył. Jest to podstawowy kod mechanizmu i jego dziesięć linii. Napisz, jak chcesz. Co powiesz na to?
public IEnumerable<Record> RecordsFromFile()
{
int offset = 0;
while(true)
{
Record record = Read(offset);
if (record == null) yield break;
yield return record;
offset += 1;
}
}
Teraz, gdy manipulujemy leniwie obliczoną sekwencją rekordów, możliwe są wszelkiego rodzaju scenariusze:
foreach(Record record in RecordsFromFile().Take(10))
DoWork(record);
foreach(Record record in RecordsFromFile().OrderBy(r=>r.LastName))
DoWork(record);
foreach(Record record in RecordsFromFile().Where(r=>r.City == "London")
DoWork(record);
I tak dalej.
Za każdym razem, gdy piszesz pętlę, zadaj sobie pytanie: „czy ta pętla czyta się jak mechanizm czy jak znaczenie kodu?” Jeśli odpowiedź brzmi „jak mechanizm”, spróbuj przenieść ten mechanizm do własnej metody i napisz kod, aby znaczenie było bardziej widoczne.