সি # তে অ্যাসিঙ্ক আচরণের প্রতিনিধিত্ব করার প্যাটার্ন


9

আমি এমন একটি ক্লাস ডিজাইনের চেষ্টা করছি যা অ্যাসিক্রোনাস প্রসেসিংয়ের উদ্বেগ যুক্ত করার ক্ষমতা প্রকাশ করে। সিঙ্ক্রোনাস প্রোগ্রামিং এ এটির মতো দেখতে

   public class ProcessingArgs : EventArgs
   {
      public int Result { get; set; }
   } 

   public class Processor 
   {
        public event EventHandler<ProcessingArgs> Processing { get; }

        public int Process()
        {
            var args = new ProcessingArgs();
            Processing?.Invoke(args);
            return args.Result;
        }
   }


   var processor = new Processor();
   processor.Processing += args => args.Result = 10;
   processor.Processing += args => args.Result+=1;
   var result = processor.Process();

একটি অ্যাসিঙ্ক্রোনাস বিশ্বে, যেখানে প্রতিটি উদ্বেগকে কোনও কাজ ফেরতের দরকার হতে পারে, এটি এত সহজ নয়। আমি এটি বেশ কয়েকটি উপায়ে দেখেছি, তবে লোকেরা খুঁজে পাওয়া কোনও সেরা অনুশীলন থাকলে আমি আগ্রহী। একটি সহজ সম্ভাবনা হ'ল

 public class Processor 
   {
        public IList<Func<ProcessingArgs, Task>> Processing { get; } =new List<Func<ProcessingArgs, Task>>();

        public async Task<int> ProcessAsync()
        {
            var args = new ProcessingArgs();
            foreach(var func in Processing) 
            {
                await func(args);
            }
            return args.Result
        }
   }

লোকেরা এর জন্য কিছু "মানক" অবলম্বন করেছে? আমি জনপ্রিয় API গুলি জুড়ে পর্যবেক্ষণ করেছি এমন একটি ধারাবাহিক পদ্ধতির বলে মনে হচ্ছে না।


আপনি কী করার চেষ্টা করছেন এবং কেন করছেন তা সম্পর্কে আমি অনিশ্চিত।
Nkosi

আমি বাস্তবায়নের উদ্বেগগুলি বহিরাগত পর্যবেক্ষকের কাছে অর্পণ করার চেষ্টা করছি (পলিমারফিজমের মতো এবং উত্তরাধিকারের উপর গঠনের ইচ্ছা)। মূলত সমস্যাযুক্ত উত্তরাধিকার শৃঙ্খলা এড়ানোর জন্য (এবং বাস্তবে এটি অসম্ভব কারণ এটির জন্য একাধিক উত্তরাধিকারের প্রয়োজন হবে)।
জেফ

উদ্বেগগুলি কি কোনও উপায়ে সম্পর্কিত এবং সেগুলি কি ক্রমানুসারে বা সমান্তরালে প্রক্রিয়াজাত হবে?
Nkosi

তারা মনে হয় অ্যাক্সেস ভাগ করে ProcessingArgsনিচ্ছে তাই আমি সে সম্পর্কে বিভ্রান্ত ছিলাম।
Nkosi

1
এটাই হ'ল প্রশ্নের মূল বিষয়। ইভেন্টগুলি কোনও কাজ ফেরত দিতে পারে না। এমনকি আমি যদি কোনও প্রতিনিধি ব্যবহার করি যা টি এর কোনও টাস্ক ফেরত দেয় তবে ফলাফলটি হারিয়ে যাবে
জেফ

উত্তর:


2

অ্যাসিক্রোনাস বাস্তবায়নের উদ্বেগগুলি পরিচালনা করতে নিম্নলিখিত প্রতিনিধি ব্যবহার করা হবে

public delegate Task PipelineStep<TContext>(TContext context);

মন্তব্য থেকে এটি ইঙ্গিত করা হয়েছিল

একটি নির্দিষ্ট উদাহরণ হ'ল "লেনদেন" (এলওবি কার্যকারিতা) সম্পূর্ণ করতে একাধিক পদক্ষেপ / কাজ যুক্ত করা হচ্ছে

নিম্নলিখিত শ্রেণিটি। নেট কোর মিডলওয়্যারের অনুরূপ সাবলীল উপায়ে এই পদক্ষেপগুলি পরিচালনা করতে একটি প্রতিনিধি গঠনের অনুমতি দেয়

public class PipelineBuilder<TContext> {
    private readonly Stack<Func<PipelineStep<TContext>, PipelineStep<TContext>>> steps =
        new Stack<Func<PipelineStep<TContext>, PipelineStep<TContext>>>();

    public PipelineBuilder<TContext> AddStep(Func<PipelineStep<TContext>, PipelineStep<TContext>> step) {
        steps.Push(step);
        return this;
    }

    public PipelineStep<TContext> Build() {
        var next = new PipelineStep<TContext>(context => Task.CompletedTask);
        while (steps.Any()) {
            var step = steps.Pop();
            next = step(next);
        }
        return next;
    }
}

নিম্নলিখিত এক্সটেনশনটি মোড়কে ব্যবহার করে সহজ ইন-লাইন সেটআপের অনুমতি দেয়

public static class PipelineBuilderAddStepExtensions {

    public static PipelineBuilder<TContext> AddStep<TContext>
        (this PipelineBuilder<TContext> builder,
        Func<TContext, PipelineStep<TContext>, Task> middleware) {
        return builder.AddStep(next => {
            return context => {
                return middleware(context, next);
            };
        });
    }

    public static PipelineBuilder<TContext> AddStep<TContext>
        (this PipelineBuilder<TContext> builder, Func<TContext, Task> step) {
        return builder.AddStep(async (context, next) => {
            await step(context);
            await next(context);
        });
    }

    public static PipelineBuilder<TContext> AddStep<TContext>
        (this PipelineBuilder<TContext> builder, Action<TContext> step) {
        return builder.AddStep((context, next) => {
            step(context);
            return next(context);
        });
    }
}

অতিরিক্ত মোড়কের জন্য প্রয়োজনীয় হিসাবে এটি আরও বাড়ানো যেতে পারে।

কার্যনির্বাহী প্রতিনিধিটির ব্যবহারের একটি উদাহরণ নিম্নলিখিত পরীক্ষায় প্রদর্শিত হয়

[TestClass]
public class ProcessBuilderTests {
    [TestMethod]
    public async Task Should_Process_Steps_In_Sequence() {
        //Arrange
        var expected = 11;
        var builder = new ProcessBuilder()
            .AddStep(context => context.Result = 10)
            .AddStep(async (context, next) => {
                //do something before

                //pass context down stream
                await next(context);

                //do something after;
            })
            .AddStep(context => { context.Result += 1; return Task.CompletedTask; });

        var process = builder.Build();

        var args = new ProcessingArgs();

        //Act
        await process.Invoke(args);

        //Assert
        args.Result.Should().Be(expected);
    }

    public class ProcessBuilder : PipelineBuilder<ProcessingArgs> {

    }

    public class ProcessingArgs : EventArgs {
        public int Result { get; set; }
    }
}

সুন্দর কোড।
জেফ

আপনি কি পরবর্তী অপেক্ষা করার পরে পদক্ষেপের অপেক্ষায় থাকতে চান না? আমার ধারণা এটি নির্ভর করে যদি অ্যাডের দ্বারা বোঝানো হয় যে আপনি যুক্ত করা হয়েছে এমন কোনও কোডের আগে আপনি কোড প্রয়োগ করতে পারেন। যেভাবে এটি হয় এটি একটি "সন্নিবেশ" এর মতো
জেফ

1
@ জেফ পদক্ষেপগুলি পাইপলাইনে যুক্ত হওয়ার সাথে সাথে তা নির্ধারিতভাবে কার্যকর করা হয়। ডিফল্ট ইনলাইন সেটআপ আপনাকে ম্যানুয়ালি পরিবর্তিত করতে দেয় যদি আপনি চান যদি স্রোত ব্যাক আপ করার পথে পোস্ট ক্রিয়াকলাপগুলি করা হয়
Nkosi

আমি যদি প্রসঙ্গ নির্ধারণের পরিবর্তে ফলাফল হিসাবে টি টাস্কটি ব্যবহার করতে চাই তবে আপনি কীভাবে এটি ডিজাইন / পরিবর্তন করবেন? ফলাফল? আপনি কি কেবল স্বাক্ষরগুলি আপডেট করবেন এবং একটি সন্নিবেশ পদ্ধতি যুক্ত করবেন (কেবল যুক্ত করার পরিবর্তে) যাতে কোনও মিডলওয়্যার তার ফলাফলটি অন্য মিডলওয়্যারের সাথে যোগাযোগ করতে পারে?
জেফ

1

আপনি যদি এটি প্রতিনিধি হিসাবে রাখতে চান তবে আপনি এটি করতে পারেন:

public class Processor
{
    public event Func<ProcessingArgs, Task> Processing;

    public async Task<int?> ProcessAsync()
    {
        if (Processing?.GetInvocationList() is Delegate[] processors)
        {
            var args = new ProcessingArgs();
            foreach (Func<ProcessingArgs, Task> processor in processors)
            {
                await processor(args);
            }
            return args.Result;
        }
        else return null;
    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.