使这项工作起作用的流畅对象模型是什么?
What is the fluent object model to make this work?
作为编写流畅 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
在里面,因为 return 类型不是直接 IEnumerable<T>
(虽然它继承自它)。这让精神上的扳手陷入了困境。扩展方法的实现是什么样的?
这是我的尝试。我重命名了接口只是为了我的理解,因为我一起破解它。
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
不用担心序列。只是在价值观上做。
您完全可以使用扩展方法来做到这一点。事情是这样的:
- 不知何故
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()...
.
同样,很容易将操作提升到任何其他monad。说,可以为空。我们有一个将价值观转化为行动的设备。将可空值转换为动作或无动作的设备是什么?它内置于语言中:x?.Begin()...
假设您希望将操作应用于 Task<int>
;将未来价值转化为未来行动的设备是什么? async Task DoIt(Task<T> task) { (await task).Begin()...
.
等等。 对值而不是提升值执行操作;使用语言的内置提升操作将您的操作应用于提升值以产生提升动作。
作为编写流畅 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
在里面,因为 return 类型不是直接 IEnumerable<T>
(虽然它继承自它)。这让精神上的扳手陷入了困境。扩展方法的实现是什么样的?
这是我的尝试。我重命名了接口只是为了我的理解,因为我一起破解它。
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
不用担心序列。只是在价值观上做。
您完全可以使用扩展方法来做到这一点。事情是这样的:
- 不知何故
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()...
.
同样,很容易将操作提升到任何其他monad。说,可以为空。我们有一个将价值观转化为行动的设备。将可空值转换为动作或无动作的设备是什么?它内置于语言中:x?.Begin()...
假设您希望将操作应用于 Task<int>
;将未来价值转化为未来行动的设备是什么? async Task DoIt(Task<T> task) { (await task).Begin()...
.
等等。 对值而不是提升值执行操作;使用语言的内置提升操作将您的操作应用于提升值以产生提升动作。