Mam następującą funkcję, aby uzyskać błędy walidacji karty. Moje pytanie dotyczy radzenia sobie z GetErrors. Obie metody mają ten sam typ zwrotu IEnumerable<ErrorInfo>
.
private static IEnumerable<ErrorInfo> GetErrors(Card card)
{
var errors = GetMoreErrors(card);
foreach (var e in errors)
yield return e;
// further yield returns for more validation errors
}
Czy można zwrócić wszystkie błędy GetMoreErrors
bez konieczności ich wyliczania?
Myśląc o tym, jest to prawdopodobnie głupie pytanie, ale chcę się upewnić, że się nie pomylę.
c#
ienumerable
yield
yield-return
John Oxley
źródło
źródło
GetCardProductionValidationErrorsFor
?Odpowiedzi:
To zdecydowanie nie jest głupie pytanie i jest to coś, co F # obsługuje
yield!
dla całej kolekcji w porównaniuyield
z pojedynczym elementem. (Może to być bardzo przydatne w przypadku rekurencji ogonowej ...)Niestety nie jest obsługiwany w C #.
Jeśli jednak masz kilka metod, z których każda zwraca an
IEnumerable<ErrorInfo>
, możesz użyć,Enumerable.Concat
aby uprościć kod:Jest jednak jedna bardzo ważna różnica między tymi dwiema implementacjami: ta wywoła wszystkie metody natychmiast , nawet jeśli będzie używać zwracanych iteratorów tylko po jednej naraz. Twój istniejący kod będzie czekał, aż zostanie zapętlony,
GetMoreErrors()
zanim zapyta o kolejne błędy.Zwykle nie jest to ważne, ale warto zrozumieć, co się stanie i kiedy.
źródło
GetOtherErrors()
(etc) są odroczenia ich wyniki (jak są one realizowane za pomocą iteratora bloki). Spróbuj je zmienić, aby zwrócić nową tablicę lub coś w tym rodzaju, a zobaczysz, o co mi chodzi.Możesz skonfigurować wszystkie źródła błędów w ten sposób (nazwy metod zapożyczone z odpowiedzi Jona Skeeta).
Następnie możesz je powtarzać w tym samym czasie.
Alternatywnie możesz spłaszczyć źródła błędów za pomocą
SelectMany
.Wykonanie metod w również
GetErrorSources
będzie opóźnione.źródło
Wymyśliłem krótki
yield_
fragment:Oto fragment kodu XML:
źródło
yield!
, jak w F #.Nie widzę nic złego w twojej funkcji, powiedziałbym, że robi to, co chcesz.
Pomyśl o Yield jako o zwracaniu elementu w końcowym wyliczeniu za każdym razem, gdy jest on wywoływany, więc jeśli masz go w takiej pętli foreach, za każdym razem, gdy jest wywoływany, zwraca 1 element. Masz możliwość umieszczania instrukcji warunkowych w swoim foreach, aby filtrować zestaw wyników. (po prostu nie poddając się kryteriom wykluczenia)
Jeśli dodasz kolejne plony później w metodzie, będzie on nadal dodawać 1 element do wyliczenia, umożliwiając wykonanie takich czynności jak ...
źródło
Jestem zaskoczony, że nikt nie pomyślał o poleceniu prostej metody rozszerzenia,
IEnumerable<IEnumerable<T>>
aby ten kod zachował odroczone wykonanie. Jestem fanem odroczonego wykonania z wielu powodów, jednym z nich jest to, że ślad pamięci jest niewielki nawet dla ogromnych wyliczeń.I możesz tego użyć w swoim przypadku w ten sposób
Podobnie możesz pozbyć się funkcji opakowującej
DoGetErrors
i po prostu przejśćUnWrap
do callsite.źródło
DoGetErrors(card).SelectMany(x => x)
robi to samo i zachowuje odroczone zachowanie. Dokładnie to sugeruje Adam w swojej odpowiedzi .Tak, istnieje możliwość natychmiastowego zwrócenia wszystkich błędów. Po prostu zwróć
List<T>
lubReadOnlyCollection<T>
.Zwracając
IEnumerable<T>
i zwracasz sekwencję czegoś. Na pozór może wydawać się to identyczne jak zwrot kolekcji, ale istnieje kilka różnic, o których należy pamiętać.Kolekcje
Sekwencje
IEnumerable<T>
pozwala na leniwą ocenę, a zwracanieList<T>
nie).źródło