代码之家  ›  专栏  ›  技术社区  ›  Bryan Boettcher

什么是fluent对象模型来实现这一点?

  •  15
  • Bryan Boettcher  · 技术社区  · 7 年前

    作为编写fluent 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> (尽管它继承自它)。这给齿轮带来了精神上的冲击。扩展方法的实现是什么样子的?

    2 回复  |  直到 7 年前
        1
  •  13
  •   Eric Lippert    7 年前

    不要担心序列。只要根据价值观来做就行了。

    您可以完全使用扩展方法来实现这一点。事情是这样的:

    • 以某种方式 Otherwise 需要知道之前是否有 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()... .

    类似地,很容易将操作提升到任何其他monad。比如说,可以为null。我们有一个将价值观转化为行动的装置。什么是将可空值转换为动作或不动作的设备?它内置于语言中: x?.Begin()...

    假设您希望将您的操作应用于 Task<int> ; 什么是将未来价值转化为未来行动的装置? async Task DoIt(Task<T> task) { (await task).Begin()... .

    等等 在价值观上执行操作,而不是在提升的价值观上

        2
  •  2
  •   Nkosi    7 年前

    这是我的尝试。我重新命名了界面,只是为了理解,因为我把它拼凑在一起了。

    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