C\#파이프 프로 그래 밍 깊이 이해
C\#프로 그래 밍 에서 파이프 프로 그래 밍(Pipeline Style programming)은 사실 존재 한 지 오래 되 었 고 가장 뚜렷 한 것 은 우리 가 자주 사용 하 는 LINQ 이다.DotNetCore 세계 에 들 어간 후에 이런 프로 그래 밍 방식 은 더욱 뚜렷 해 졌 다.예 를 들 어 각종 미들웨어 의 사용 이다.이런 프로 그래 밍 방식 을 사용 함으로써 코드 의 유지 가능성 을 크게 향상 시 키 고 업무 의 조합 방식 을 최적화 시 켰 다.
파이프 프로 그래 밍 은 다음 과 같은 장점 을 가진다.
이 예시 에서,우 리 는 double->int->string 의 형식 변환 파 이 프 를 구축 하여 목표 데 이 터 를 최종 적 으로 문자열 로 변환 합 니 다.
public interface IPipelineStep<INPUT, OUTPUT>
{
OUTPUT Process(INPUT input);
}
public class DoubleToIntStep : IPipelineStep<double, int>
{
public int Process(double input)
{
return Convert.ToInt32(input);
}
}
public class IntToStringStep : IPipelineStep<int, string>
{
public string Process(int input)
{
return input.ToString();
}
}
public static class PipelineStepExtensions
{
public static OUTPUT Step<INPUT, OUTPUT>(this INPUT input, IPipelineStep<INPUT, OUTPUT> step)
{
return step.Process(input);
}
}
class Program
{
static void Main(string[] args)
{
double input = 1024.1024;
//
string result = input.Step(new DoubleToIntStep())
.Step(new IntToStringStep());
Console.WriteLine(result);
}
}
이때,우 리 는 double 형식의 데 이 터 를 string 형식 으로 바 꾸 는 데 성공 했다.상기 예 시 를 소개 함으로써 우 리 는 파이프 식 프로 그래 밍 을 간단하게 정의 기능 인터페이스->실현 기능 함수->조립 기능 함수 로 요약 할 수 있다.의존 주입
상기 코드 는 일반적인 상황 에서 정상적으로 작 동 할 수 있 지만 주입(DI)에 의존 하 는 방식 으로 주입 하 기 를 원한 다 면 우 리 는 우리 의 파 이 프 를 조립 하여 포장 하여 하나의 통 일 된 서비스 로 시스템 에 주입 하 는 데 편리 하도록 해 야 한다.
public abstract class Pipeline<INPUT,OUTPUT>
{
public Func<INPUT, OUTPUT> PipelineSteps { get; protected set; }
public OUTPUT Process(INPUT input)
{
return PipelineSteps(input);
}
}
public class TrivalPipeline : Pipeline<double, string>
{
public TrivalPipeline()
{
PipelineSteps = input => input.Step(new DoubleToIntSetp())
.Step(new IntToStringStep());
}
}
마지막 으로 우 리 는 TrivalPipeline
이 구체 적 인 파 이 프 를 우리 시스템 에 주입 할 수 있다.마찬가지 로 우 리 는 직접 사용 할 수 있 습 니 다.예제 코드 는 다음 과 같 습 니 다.
class Program
{
static void Main(string[] args)
{
double input = 1024.1024;
// Microsoft.Extensions.DependencyInjection
var services = new ServiceCollection();
services.AddTransient<TrivalPipeline>();
var provider = services.BuildServiceProvider();
var trival = provider.GetService<TrivalPipeline>();
string result = trival.Process(input);
Console.WriteLine(result);
}
}
조건식 조립상기 두 예제 코드 가 보 여 준 파이프 조립 식 은 어떠한 조건 도 제한 하지 않 습 니 다.매개 변수 가 합 법 적 이 든 그렇지 않 든 모두 이렇게 파 이 프 를 조립 합 니 다.그러나 실제 개발 과정 에서 우 리 는 일정한 업무 모듈 에 대해 조건 부 조립 을 해 야 하기 때문에 이 럴 때 우 리 는 우리 의 코드 를 보완 해 야 합 니 다.
우선,우 리 는 위의
Pipeline<INPUT,OUTPUT>
종 류 를 수정 하여 계승IPipelineStep<INPUT, OUTPUT>
인 터 페 이 스 를 수정 해 야 한다.예시 코드 는 다음 과 같다.
public abstract class Pipeline<INPUT, OUTPUT> : IPipelineStep<INPUT, OUTPUT>
{
public Func<INPUT, OUTPUT> PipelineSteps { get; protected set; }
public OUTPUT Process(INPUT input)
{
return PipelineSteps(input);
}
}
public class OptionalStep<INPUT, OUTPUT> : IPipelineStep<INPUT, OUTPUT> where INPUT : OUTPUT
{
private readonly IPipelineStep<INPUT, OUTPUT> _step;
private readonly Func<INPUT, bool> _choice;
public OptionalStep(Func<INPUT,bool> choice,IPipelineStep<INPUT,OUTPUT> step)
{
_choice = choice;
_step = step;
}
public OUTPUT Process(INPUT input)
{
return _choice(input) ? _step.Process(input) : input;
}
}
public class ThisStepIsOptional : IPipelineStep<double, double>
{
public double Process(double input)
{
return input * 10;
}
}
public class PipelineWithOptionalStep : Pipeline<double, double>
{
public PipelineWithOptionalStep()
{
// 1024, ThisStepIsOptional()
PipelineSteps = input => input.Step(new OptionalStep<double, double>(i => i > 1024, new ThisStepIsOptional()));
}
}
class Program
{
static void Main(string[] args)
{
PipelineWithOptionalStep step = new PipelineWithOptionalStep();
Console.WriteLine(step.Process(1024.1024)); // 10241.024
Console.WriteLine(step.Process(520.520)); // 520.520
}
}
사건 감청때때로 우 리 는 우리 파이프 에서 실행 되 는 모든 단계 가 시작 과 끝 날 때 상층 모듈 은 해당 하 는 사건 통 지 를 받 을 수 있 기 를 희망 한다.이때 우 리 는 우리 의 파이프 포장 기 를 바 꾸 어 이 수 요 를 지원 하도록 해 야 한다.
우선,우 리 는 이벤트 감청 을 지원 하 는 구체 적 인 기능 류 를 실현 해 야 한다.예시 코드 코드 는 다음 과 같다.
public class EventStep<INPUT, OUTPUT> : IPipelineStep<INPUT, OUTPUT>
{
public event Action<INPUT> OnInput;
public event Action<OUTPUT> OnOutput;
private readonly IPipelineStep<INPUT, OUTPUT> _innerStep;
public EventStep(IPipelineStep<INPUT,OUTPUT> innerStep)
{
_innerStep = innerStep;
}
public OUTPUT Process(INPUT input)
{
OnInput?.Invoke(input);
var output = _innerStep.Process(input);
OnOutput?.Invoke(output);
return output;
}
}
public static class PipelineStepEventExtensions
{
public static OUTPUT Step<INPUT, OUTPUT>(this INPUT input, IPipelineStep<INPUT, OUTPUT> step, Action<INPUT> inputEvent = null, Action<OUTPUT> outputEvent = null)
{
if (inputEvent != null || outputEvent != null)
{
var eventDecorator = new EventStep<INPUT, OUTPUT>(step);
eventDecorator.OnInput += inputEvent;
eventDecorator.OnOutput += outputEvent;
return eventDecorator.Process(input);
}
return step.Process(input);
}
}
public class DoubleStep : IPipelineStep<int, int>
{
public int Process(int input)
{
return input * input;
}
}
class Program
{
static void Main(string[] args)
{
var input = 10;
Console.WriteLine($"Input Value:{input}[{input.GetType()}]");
var pipeline = new EventStep<int, int>(new DoubleStep());
pipeline.OnInput += i => Console.WriteLine($"Input Value:{i}");
pipeline.OnOutput += o => Console.WriteLine($"Output Value:{o}");
var output = pipeline.Process(input);
Console.WriteLine($"Output Value: {output} [{output.GetType()}]");
Console.WriteLine("\r
");
// :
Console.WriteLine(10.Step(new DoubleStep(), i =>
{
Console.WriteLine($"Input Value:{i}");
},
o =>
{
Console.WriteLine($"Output Value:{o}");
}));
}
}
출력 결 과 는 다음 그림 과 같 습 니 다.반복 실행 가능
교체 실행 이란 우리 의 파이프 에 여러 기능 모듈 이 등록 되 어 있 을 때 한꺼번에 실행 되 기 때문에 기능 모듈 이 아니 라 매번 하나의 기능 만 수행 하 는 것 을 말한다.후속 기능 은 다음 에 이 파이프 에 대응 하 는 코드 블록 을 실행 할 때 이 파이프 에 있 는 기능 모듈 이 실 행 될 때 까지 계속 실행 된다.이 특성 은 주로 yield return 을 통 해 이 루어 집 니 다.
우선,우 리 는 이 특성의 파이프 포장 기 류 를 실현 해 야 한다.예시 코드 는 다음 과 같다.
public class LoopStep<INPUT, OUTPUT> : IPipelineStep<IEnumerable<INPUT>, IEnumerable<OUTPUT>>
{
private readonly IPipelineStep<INPUT, OUTPUT> _internalStep;
public LoopStep(IPipelineStep<INPUT,OUTPUT> internalStep)
{
_internalStep = internalStep;
}
public IEnumerable<OUTPUT> Process(IEnumerable<INPUT> input)
{
foreach (INPUT item in input)
{
yield return _internalStep.Process(item);
}
//
//return from INPUT item in input
// select _internalStep.Process(item);
}
}
public static class PipelineStepLoopExtensions
{
public static IEnumerable<OUTPUT> Step<INPUT, OUTPUT>(this IEnumerable<INPUT> input, IPipelineStep<INPUT, OUTPUT> step)
{
LoopStep<INPUT, OUTPUT> loopDecorator = new LoopStep<INPUT, OUTPUT>(step);
return loopDecorator.Process(input);
}
}
class Program
{
static void Main(string[] args)
{
var list = Enumerable.Range(0, 10);
foreach (var item in list.Step(new DoubleStep()))
{
Console.WriteLine(item);
}
}
}
총결산이 글 은 여기까지 입 니 다.당신 에 게 도움 이 되 기 를 바 랍 니 다.또한 당신 이 우리 의 더 많은 내용 에 관심 을 가 질 수 있 기 를 바 랍 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
WebView2를 Visual Studio 2017 Express에서 사용할 수 있을 때까지Evergreen .Net Framework SDK 4.8 VisualStudio2017에서 NuGet을 사용하기 때문에 패키지 관리 방법을 packages.config 대신 PackageReference를 사용해야...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.