Что такое текущая объектная модель, чтобы сделать эту работу?

Как практика написания бедных 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