Что такое текущая объектная модель, чтобы сделать эту работу?
Как практика написания бедных API-интерфейсов, я думал, что сделаю следующую компиляцию и запуск:
static void Main(string[] args)
{
Enumerable.Range(1, 100)
.When(i => i % 3 == 0).Then(i => Console.WriteLine("fizz"))
.When(i => i % 5 == 0).Then(i => Console.WriteLine("buzz"))
.Otherwise(i => Console.WriteLine(i))
.Run();
Console.ReadLine();
}
Идея .When
проверяет каждый элемент в перечислении, и если он передает предикат, чтобы запустить действие. Если предикат терпит неудачу, элемент передается по цепочке.
График, с которым я столкнулся, был:
public static class EnumerableExtensions
{
public static IConditionalEnumerable<T> When<T>(this IEnumerable<T> items, Predicate<T> test, Action<T> action)
{
}
public static IResolvedEnumerable<T> Then<T>(this IConditionalEnumerable<T> items, Predicate<T> test, Action<T> action)
{
}
public static void Run<T>(this IEnumerable<T> items)
{
foreach (var item in items) ;
}
}
public interface IConditionalEnumerable<T> : IEnumerable<T>
{
IResolvedEnumerable<T> Then<T>(IConditionalEnumerable<T> items, Action<T> action);
}
public interface IResolvedEnumerable<T> : IEnumerable<T>
{
IEnumerable<T> Otherwise(Action<T> behavior);
}
Я столкнулся с проблемой - When
не может foreach / yield return
внутри него, потому что тип возврата не является прямым IEnumerable<T>
(хотя он наследует его). Это заставило мысленный ключ в шестерню. Как выглядит реализация методов расширения?
Ответы
Ответ 1
Не беспокойтесь о последовательностях. Просто делайте это по значениям.
Вы можете сделать это полностью с помощью методов расширения. Здесь вещь:
- Как-то
Otherwise
нужно знать, выполнялся ли предыдущий Then
.
- Поэтому почему-то каждый
When
должен знать, выполняются ли все предыдущие Then
.
- Каждый
Then
должен знать, было ли предыдущее When
истинным или ложным.
Здесь скелет:
static class X
{
public enum WhenState { DoIt, DoNot }
public enum ThenState { DidIt, DidNot }
public static (T, ThenState) Begin<T>(this T item) { ... }
public static (T, WhenState, ThenState) When<T>(
this (T item, ThenState then) tuple,
Func<T, bool> p) { ... }
public static (T, ThenState) Then<T>(
this (T item, WhenState when, ThenState then) tuple,
Action<T> a) { ... }
public static void Otherwise<T>(
this (T item, ThenState then) tuple,
Action<T> a) { ... }
Как только вы реализуете эти методы расширения, вы можете сделать:
3.Begin().When(x => x % 3 == 0).Then( ... )
и т.д.
Как только вы приобретете это, вы легко поднимите операцию на последовательности. У нас есть устройство, которое превращает ценности в действия; что такое устройство, которое превращает последовательность значений в последовательность действий? Он встроен в язык: foreach(var item in items) item.Begin()...
.
Аналогично, легко поднять операцию на любую другую монаду. Скажем, с нулевым значением. У нас есть устройство, которое превращает ценности в действия. Что такое устройство, чтобы превратить значение NULL в действие или действие? Он встроен в язык: x?.Begin()...
Скажите, что вы хотите применить свою операцию к Task<int>
; что такое устройство, которое превращает будущую ценность в будущие действия? async Task DoIt(Task<T> task) { (await task).Begin()...
.
И так далее. Реализовать свою операцию над значениями, а не при поднятых значениях; используйте встроенные операции по подъему языка, чтобы применить свою операцию к поднятому значению, чтобы произвести поднятое действие.
Ответ 2
Вот моя попытка. Я переименовал интерфейсы только для своего понимания, поскольку я взломал его вместе.
public interface IWhen<T> {
IThen<T> Then(Action<T> action);
}
public interface IThen<T> : IRun {
IWhen<T> When(Func<T, bool> test);
IRun Otherwise(Action<T> action);
}
public interface IRun {
void Run();
}
public interface IRule<T> {
Func<T, bool> Predicate { get; }
Action<T> Invoke { get; }
}
И создал следующие реализации.
public class Rule<T> : IRule<T> {
public Rule(Func<T, bool> predicate, Action<T> action) {
this.Predicate = predicate;
this.Invoke = action;
}
public Func<T, bool> Predicate { get; private set; }
public Action<T> Invoke { get; private set; }
}
public class Then<T> : IThen<T> {
private Queue<IRule<T>> rules = new Queue<IRule<T>>();
private IEnumerable<T> source;
public Then(IEnumerable<T> source, Queue<IRule<T>> rules) {
this.source = source;
this.rules = rules;
}
public IWhen<T> When(Func<T, bool> test) {
var temp = new When<T>(source, test, rules);
return temp;
}
public void Run() {
foreach (var item in source) {
var rule = rules.FirstOrDefault(r => r.Predicate(item));
if (rule == null) continue;
rule.Invoke(item);
}
}
public IRun Otherwise(Action<T> action) {
var rule = new Rule<T>(s => true, action);
rules.Enqueue(rule);
return new Then<T>(source, rules);
}
}
public class When<T> : IWhen<T> {
private Queue<IRule<T>> rules;
private Func<T, bool> test;
private IEnumerable<T> source;
public When(IEnumerable<T> source, Func<T, bool> test, Queue<IRule<T>> rules = null) {
this.source = source;
this.test = test;
this.rules = rules ?? new Queue<IRule<T>>();
}
public IThen<T> Then(Action<T> action) {
var rule = new Rule<T>(test, action);
rules.Enqueue(rule);
var then = new Then<T>(source, rules);
return then;
}
}
Далее был добавлен метод расширения.
public static class EnumerableExtensions {
public static IWhen<T> When<T>(this IEnumerable<T> source, Func<T, bool> test) {
var temp = new When<T>(source, test);
return temp;
}
}
И с этим следующий пример в .NET Fiddle
public class Program {
public static void Main() {
Enumerable.Range(1, 10)
.When(i => i % 3 == 0).Then(i => Console.WriteLine("fizz"))
.When(i => i % 5 == 0).Then(i => Console.WriteLine("buzz"))
.Otherwise(i => Console.WriteLine(i))
.Run();
}
}
произведено
1
2
fizz
4
buzz
fizz
7
8
fizz
buzz