首页 > 解决方案 > 为流畅的语法自动设置 `TInput` 等于 `TOutput`

问题描述

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

 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等于并在构建时验证类型?TOutputThenTransform<TInput, TOutput>

期望的结果

 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

标签: c#.netfluent

解决方案


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

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.Configuration https://github.com/dotnet/extensions/tree/release/3.1/src/Configuration


推荐阅读