【问题标题】:Automatically set `TInput` equal to `TOutput` for the fluent syntax为流畅的语法自动设置 `TInput` 等于 `TOutput`
【发布时间】:2020-12-26 00:49:45
【问题描述】:

我正在使用流利的语法构建我的个人自动化框架。这是一个管道示例

 var pipeline = Core.Runner.CreatePipeline()
                .BeginMany(Sources.Csv(@"..."))
                .ThenTransform<dynamic,string[]>(record=> record.id.Split(":"))
                .ThenTransform<string[], (string, string)>(record => (record[0], record[1]))
                .ThenTransform<(string, string), dynamic>(new {...})

我想知道有什么方法可以提高可用性并自动将TInput 设置为链中的下一个TOutput 等于ThenTransform&lt;TInput, TOutput&gt; 并在构建时验证类型?

期望的结果

 var pipeline = Core.Runner.CreatePipeline()
                .BeginMany(Sources.Csv(@"..."))
                .ThenTransform<string[]>(record=> record.id.Split(":")) // TInput is dynamic, TOutput is string[]
                .ThenTransform<(string, string)>(record => (record[0], record[1])) // TInput is string[], TOuput is (string,string)
                .ThenTransform<dynamic>(new {...}) // etc

因为 lambda 知道返回类型,所以可能会有更好的结果

 var pipeline = Core.Runner.CreatePipeline()
                .BeginMany(Sources.Csv(@"..."))
                .ThenTransform(record=> record.id.Split(":")) // TInput is dynamic, TOutput is string[]
                .ThenTransform(record => (record[0], record[1])) // TInput is string[], TOuput is (string,string)
                .ThenTransform(new {...}) // etc

【问题讨论】:

  • 您似乎正在尝试重新创建 LINQ。 ThenTransformSelect 有何不同?
  • 在那张纸条上,您是否考虑过 LINQ 使用的 Cast&lt;&gt;() 方法? BeginMany(...).ThenTransform(record =&gt; record.id).Cast&lt;string&gt;().ThenTransform(s =&gt; s.Split(":"))...
  • @StriplingWarrior 我的管道使用源而不是集合,它是数据流的流畅包装器,但类似的逻辑,是的。我不需要在这里进行强制转换,因为我已经从 lambda 获得了所需的类型。
  • 你说你已经从 lambda 获得了所需的类型,但实际上你没有,因为任何以 dynamic 开头的东西都会产生另一个 dynamic 直到你明确地将它转换为其他东西.在您当前的语法中,您通过提供通用方法参数来转换它。如果你想避免这种情况,你可以说.ThenTransform(record =&gt; (string)record.id),这样你就不需要演员了。
  • 另外,我不相信您没有重新发明 LINQ。 PLINQ 就是将并行管道包装在一个流畅的接口中。 Reactive Extensions 对异步流处理执行相同的操作。两者都包含 LINQ 样式的语法,它允许人们使用 LINQ 表达式语法。需要考虑的事情。

标签: c# .net fluent


【解决方案1】:

您尚未在此处指定如何存储状态,但对于泛型您可以执行以下操作:

using System;

namespace ConsoleApp16
{
    class Program
    {
        static void Main(string[] args)
        {
            var pipeline = Core.Runner.CreatePipeline<dynamic>()
                .BeginMany(Sources.Csv(@"..."))
                // Type cannot be inferred from dynamic
                .ThenTransform<string[]>(record => record.id.Split(":")) 
                .ThenTransform(record => (record[0], record[1]))
                .ThenTransform(s => s.Item1);
        }
    }

    internal class Sources
    {
        public static object Csv(string s)
        {
            return new object();
        }
    }

    internal class Core
    {
        public class Runner
        {
            public static Pipeline<TInput> CreatePipeline<TInput>()
            {
                return new Pipeline<TInput>(new PipelineState());
            }
        }
    }

    internal class PipelineState
    {
        public bool MyState { get; set; }
    }

    internal class Pipeline<TInput>
    {
        private readonly PipelineState _pipelineState;

        public Pipeline(PipelineState pipelineState)
        {
            _pipelineState = pipelineState;
        }

        public Pipeline<TInput> BeginMany(object csv)
        {
            // Update state
            return this;
        }

        public Pipeline<TOutput> ThenTransform<TOutput>(Func<TInput, TOutput> func)
        {
            // Update state
            return new Pipeline<TOutput>(_pipelineState);
        }
    }
}

您可以通过使用不同方法返回的不同PipelineBuilder 类来改进这一点。例如BeginMany 可能会返回一个具有ThenTransform 方法的类,以便强制执行顺序:

using System;

namespace ConsoleApp16
{
    class Program
    {
        static void Main(string[] args)
        {
            var pipeline = Core.Runner.CreatePipeline()
                .BeginMany(Sources.Csv(@"..."))
                // Type cannot be inferred from dynamic
                .ThenTransform<string[]>(record => record.id.Split(":"))
                .ThenTransform(record => (record[0], record[1]))
                .ThenTransform(s => s.Item1)
                .Build();
        }
    }

    internal class Sources
    {
        public static Source<dynamic> Csv(string s)
        {
            return new Source<dynamic>();
        }
    }

    internal class Source<T>
    {
    }

    internal class Core
    {
        public class Runner
        {
            public static PipelineBuilder CreatePipeline()
            {
                return new PipelineBuilder(new PipelineState());
            }
        }
    }

    internal class PipelineState
    {
        public bool MyState { get; set; }
    }

    internal class PipelineBuilder
    {
        protected readonly PipelineState State;

        public PipelineBuilder(PipelineState state)
        {
            State = state;
        }

        public PipelineBuilder<TInput> BeginMany<TInput>(Source<TInput> source)
        {
            // Update state
            return new PipelineBuilder<TInput>(State);
        }

        public Pipeline Build()
        {
            // Populate from state
            return new Pipeline();
        }
    }


    internal class PipelineBuilder<TInput> : PipelineBuilder
    {
        public PipelineBuilder(PipelineState pipelineState) : base(pipelineState)
        {
        }

        public PipelineBuilder<TOutput> ThenTransform<TOutput>(Func<TInput, TOutput> func)
        {
            // Update state
            return new PipelineBuilder<TOutput>(State);
        }
    }

    internal class Pipeline
    {
    }
}

构建器模式值得研究,当与接口和扩展方法结合使用时,它可以非常强大。一个很好的例子是Microsoft.Extensions.Configurationhttps://github.com/dotnet/extensions/tree/release/3.1/src/Configuration

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-20
    • 1970-01-01
    • 2015-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多