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

C# 什么是fluent对象模型来实现这一点?,c#,functional-programming,ienumerable,C#,Functional Programming,Ienumerable,作为编写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"))

作为编写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();
}
想法是
。当
测试枚举中的每个元素时,如果它通过谓词,则运行操作。如果谓词失败,则该项沿链传递

我得出的图表是:

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);
}
公共静态类EnumerableExtensions
{
公共静态IConditionalEnumerable When(此IEnumerable项、谓词测试、操作)
{
}
公共静态IResolvedEnumerable Then(此IConditionalEnumerable项、谓词测试、操作)
{
}
公共静态无效运行(此IEnumerable items)
{
foreach(项目中的var项目);
}
}
公共接口IConditionalEnumerable:IEnumerable
{
IResolvedEnumerable Then(i条件可枚举项、操作);
}
公共接口IResolvedEnumerable:IEnumerable
{
i否则可数(动作行为);
}

我遇到了一个问题--
不能
foreach/yield return
在它里面时,因为返回类型不是直接的
IEnumerable
(尽管它继承自它)。这给齿轮带来了精神上的冲击。扩展方法的实现是什么样子的?

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

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; }
}
产生

1
2
fizz
4
buzz
fizz
7
8
fizz
buzz

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

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

  • 不知何故,
    否则
    需要知道之前的
    是否执行过
    
  • 因此,在某种程度上,每一个
    的时候都需要知道之前的
    是否都执行了
  • 然后,每个
    都需要知道前一个
    时的
    是真是假
这是骨架:

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) { ... }
等等

一旦你实现了它,那么就很容易将操作提升到序列。我们有一个将价值观转化为行动的装置;什么是将一系列值转换为一系列动作的装置?它内置于以下语言中:
foreach(items中的var项)item.Begin()…

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

假设您希望将操作应用于
任务
;什么是将未来价值转化为未来行动的装置<代码>异步任务DoIt(任务任务){(等待任务).Begin()…


依此类推。对值执行操作,而不是对提升值执行操作。使用该语言的内置提升操作将操作应用于提升值以生成提升操作。

提示:您能否构建不对序列而对单个值执行操作的相同fluent API?也就是说,您可以编写不是对
IEnumerable
的扩展,而是简单地扩展到,比如说
int
,这样您就可以对单个整数发出嘶嘶声了?如果是这样,那么将其扩展到序列(或可等待项,或可空项,或其他任何内容)是一个简单的过程。当您这样做时,您会发现问题与它是一个序列这一事实无关。问题是
否则
不知何故需要知道
的数量是否为零或非零,然后
子句是否已经执行。您可以尝试的下一个练习是:现在尝试编写一个相当于“开关”的流畅API例如,假设我们有
switch(value).Case(v=>someBool)。然后(v=>someVal)。Case(v=>someBool)。然后(v=>someVal)。否则(v=>someVal)
where
value
someVal
可以是不同的类型。你能实现它吗?然后你能提升它来处理一系列的值,并产生一系列的结果吗?这
当(…)。然后(…)
-当
时,是否可以从
返回
IEnumerable
,然后仅在布尔值为真时应用
我们的目标是生成序列
1,2,fizz,4,buzz,fizz,7,8,9,buzz,11,fizz,13,14,fizzbuzz…
这是本网站创始人之一对本文的参考。请参阅我对原始问题的评论;如果不按顺序执行,这个问题更容易解决。我们已经有了一种在v上提升动作的机制将值转换为一系列值上的一系列操作;称为“foreach”。我真的很喜欢这个。它正是我想要的:)我喜欢避免为
IEnumerable
实现这类事情的想法,因为这样做只会添加一个
foreach
循环。我得看看我的
IEnumerable
扩展,看看有没有类似的东西需要简化。@Sahuagin:你可能会问:如果我认为我们应该对值而不是提升值进行操作,为什么我们有Select、Where和SelectMany对序列而不是值进行操作。SelectMany的签名应该清楚地说明为什么它不能对值进行操作;操作的前提是它从序列中提取值,将它们转换为其他序列nces.和Select以及Where只是SelectMany的特殊版本,它们有自己的方法以方便使用。
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
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( ... )