A co jeżeli chcielibyśmy stworzyć pipeline z innym typem wejścia i wyjścia? Wtedy będziesz musiał zastosować type dynamic. Spójrz na klasę podstawową reprezentującą dany krok, która została przedstawiona w poprzednim rozwiązaniu:
public abstract class BaseStep<T> { protected BaseStep<T> _nextStep; public void SetNextStep(BaseStep<T> nextStep) { _nextStep = nextStep; } public T Process(T input) { var items = Handle(input); return _nextStep != null ? _nextStep.Process(items) : items; } protected abstract T Handle(T input); }
Zmieńmy w tej klasie type dla następnego kroku, wprowadźmy dodatkowy parametr generyczny, aby odróżnić typ wejściowy od wyjściowego.
Cała klasa bazowa musi dziedziczyć po DynamicObject.
public abstract class ProceseBase<T, TReturn> : DynamicObject { protected dynamic _nextStep; public void SetNextStep(dynamic nextStep) { _nextStep = nextStep; } public dynamic Process(T input) { var items = Handle(input); return _nextStep != null ? _nextStep.Process(items) : items; } protected abstract TReturn Handle(T input); }
Taka zmiana pozwoli nam na dowolną modyfikację zwracanych typów obiektu z pipeline.
Przykład użycia wygląda następująco. Dajmy na to, że z wygenerowanych licz, chcemy sprawdzić ile jest licz dla danej pierwszej cyfrą. Dla liczb { 1,11,13,29,32,300 } mamy dla cyfry 1 - 3 liczby, dla 2 - 1 liczba, dla 3 - 2 liczby.
Na samym początku stwórzmy klasę, która będzie reprezentowała pierwszy krok - konwersja na string.
public class ConvertToStringProcess<T> : ProceseBase<IEnumerable<T>, IEnumerable<string>> { protected override IEnumerable<string> Handle(IEnumerable<T> input) { return input.Select(x => x.ToString()); } }
Następnie krok grupujący oraz krok zliczający liczbę elementów. Każdy z tych kroków ma inny typ wejściowy i wyjściowy.
public class GroupByFirstCharProcess : ProceseBase<IEnumerable<string>, IEnumerable<IGrouping<char,string>>> { protected override IEnumerable<IGrouping<char, string>> Handle(IEnumerable<string> input) { return input.GroupBy(x => x.First()); } } public class CountItemsDictionary : ProceseBase<IEnumerable<IGrouping<char, string>>, Dictionary<char, int>> { protected override Dictionary<char, int> Handle(IEnumerable<IGrouping<char, string>> input) { return input.ToDictionary(x => x.Key, x => x.Count()); } }
A na końcu wywołanie dynamic pipelinu. Każde rozwiązanie ma jakieś plusy i minusy. W tym rozwiązaniu minusem jest używanie dynamikc :)
(tutaj, tutaj i tutaj)
var numbers= HelpClass.GenerateData<int>(x => x * x).Take(300); dynamic itemsStrings= new ConvertToStringProcess<int>(); GroupByFirstCharProcess groupedByFirstChar = new GroupByFirstCharProcess(); CountItemsDictionary countedItems = new CountItemsDictionary(); itemsStrings.SetNextStep(groupedByFirstChar); groupedByFirstChar.SetNextStep(countedItems); var countedNumbers= itemsStrings.Process(numbers);
Brak komentarzy:
Prześlij komentarz