যখন আমরা async EventHandlers নিয়ে আলোচনা করি, তখন আমাদের অনেকের মনে প্রথম যে বিষয়টি আসে তা হল এটি একমাত্র ব্যতিক্রম যা আমরা অনুমতি দিই বলে মনে হয়
যখন আমি এর আগে এই বিষয়ে লিখেছিলাম, তখন আমি উত্তেজিত ছিলাম যে আমি এমন একটি সমাধান অন্বেষণ করছিলাম যা আসলে অ্যাসিঙ্ক শূন্যতার অস্তিত্বের অনুমতি দেয় (আমার বাকি চুলগুলি টানতে না চাওয়া ছাড়া)।
আমার জন্য, এটি কিছু চতুর কৌশল সম্পর্কে ছিল যা আমরা সম্পূর্ণরূপে সমস্যা এড়ানোর জন্য সমাধান প্রদানের চেয়ে অ্যাসিঙ্ক ইভেন্টহ্যান্ডলারগুলিকে কাটিয়ে উঠতে ব্যবহার করতে পারি।
যদিও এটি বলার সাথে সাথে, নিবন্ধটিতে প্রচুর ট্র্যাকশন ছিল, যার জন্য আমি খুব কৃতজ্ঞ, এবং কিছু লোক মতামত প্রকাশ করেছে যে তারা বরং অ্যাসিঙ্ক ইভেন্টহ্যান্ডলারগুলিকে অন্য উপায়ে সমাধান করবে।
আমি ভেবেছিলাম এটি একটি দুর্দান্ত পয়েন্ট ছিল, তাই আমি একটি বিকল্প পদ্ধতি নিয়ে আসতে চেয়েছিলাম যা অ্যাসিঙ্ক শূন্যতাকে ঠিক করে না, তবে এটি আপনাকে কিছু চ্যালেঞ্জের সমাধান করার সময় এটিকে সম্পূর্ণরূপে বাতিল করতে দেয় (দেখুন আমি সেখানে কী করেছি?) async ইভেন্টহ্যান্ডলারের সাথে।
এই নিবন্ধে, আমি আরেকটি সমাধান উপস্থাপন করব যা আপনি নিজের কোডে চেষ্টা করতে পারেন। এটি কীভাবে ব্যবহার করা যেতে পারে সে সম্পর্কে আমরা আমার দৃষ্টিকোণ থেকে ভাল এবং অসুবিধাগুলিকে সম্বোধন করব যাতে আপনি সিদ্ধান্ত নিতে পারেন যে এটি আপনার ব্যবহারের ক্ষেত্রে অর্থপূর্ণ কিনা।
আপনি .NET বেহালার ডানদিকে কিছু ইন্টারঅ্যাক্টেবল কোডও খুঁজে পেতে পারেন
অ্যাসিঙ্ক ইভেন্টহ্যান্ডলারের সাথে আমরা যে সমস্যার মুখোমুখি হচ্ছি তা হল যে ইভেন্টগুলির জন্য স্বাক্ষর যা আমরা ডিফল্টরূপে C# এ সাবস্ক্রাইব করতে পারি তা এইরকম দেখায়:
void TheObject_TheEvent(object sender, EventArgs e);
এবং, আপনি লক্ষ্য করবেন যে এই স্বাক্ষরের সামনে অকার্যকর করে, আমরা ইভেন্টে সদস্যতা নেওয়ার জন্য আমাদের নিজস্ব হ্যান্ডলারে অকার্যকর ব্যবহার করতে বাধ্য হচ্ছি।
এর মানে হল যে আপনি যদি চান যে আপনার হ্যান্ডলার কখনও অ্যাসিঙ্ক/ওয়েট কোড চালাতে, তাহলে আপনাকে আপনার অকার্যকর পদ্ধতির মধ্যে অপেক্ষা করতে হবে... যা বড় ভীতিকর অ্যাসিঙ্ক অকার্যকর প্যাটার্ন উপস্থাপন করে যা আমাদের প্লেগের মতো এড়াতে বলা হয়েছে।
এবং কেন? কারণ async void ব্যতিক্রমগুলির জন্য সঠিকভাবে বুদবুদ হওয়ার ক্ষমতা ভঙ্গ করে এবং এর ফলে এক টন মাথাব্যথা হতে পারে।
আমার মতে, সহজ হল আরও ভাল... সুতরাং আপনি যদি অ্যাসিঙ্ক ভ্যায়েডের উপর আমার আগের নিবন্ধটি পড়েন এবং আপনার লক্ষ্য সত্যিই ইভেন্টহ্যান্ডলারদের সাথে মোকাবিলা করা ছিল, তাহলে এটি সাহায্য করবে।
পূর্বে উল্লিখিত শর্তের উপর ভিত্তি করে, ব্যতিক্রম হ্যান্ডলিং অ্যাসিঙ্ক শূন্যতার সীমানা ভেঙ্গে যায়। আপনি যদি এই সীমানা অতিক্রম করার জন্য বুদ্বুদ আপ করতে হবে এমন একটি ব্যতিক্রম থাকে, তাহলে আপনি একটি মজার সময় কাটাতে যাচ্ছেন।
এবং মজার দ্বারা, আমি বলতে চাচ্ছি যে আপনি যদি ডিবাগিং উপভোগ করেন কেন স্টাফ কাজ করছে না এবং আপনার কাছে স্পষ্ট ইঙ্গিত নেই যে কী ভাঙছে, তাহলে আপনি সত্যিই একটি দুর্দান্ত সময় কাটাবেন।
তাই এটি ঠিক করার সবচেয়ে সহজ উপায় কি?
আসুন একটি সাধারণ টুল ব্যবহার করে প্রথমে এই সীমানা অতিক্রম করতে সক্ষম হওয়া থেকে ব্যতিক্রমগুলি প্রতিরোধ করি: চেষ্টা করুন/ক্যাচ করুন৷
objectThatRaisesEvent.TheEvent += async (s, e) => { // if the try catch surrounds EVERYTHING in the handler, no exception can bubble up try { await SomeTaskYouWantToAwait(); } catch (Exception ex) { // TODO: put your exception handling stuff here } // no exception can escape here if the try/catch surrounds the entire handler body }
উপরের কোডে উল্লিখিত হিসাবে, আপনি যদি আপনার ইভেন্ট হ্যান্ডলারের পুরো অংশের চারপাশে একটি চেষ্টা/ক্যাচ ব্লক রাখেন, তাহলে আপনি সেই অ্যাসিঙ্ক অকার্যকর সীমানা জুড়ে বুদবুদ হওয়া থেকে কোনো ব্যতিক্রম প্রতিরোধ করতে পারেন। পৃষ্ঠে, এটি বেশ সহজ এবং এটি বাস্তবায়নের জন্য অভিনব কিছুর প্রয়োজন নেই।
সুবিধা:
অসুবিধা:
যে বলে, এই সমাধান সত্যিই সহজ, কিন্তু আমি মনে করি আমরা একটু ভাল করতে পারি।
একটি উন্নতি যা আমি মনে করি আমরা প্রাথমিকভাবে প্রস্তাবিত সমাধানটি তৈরি করতে পারি তা হল আমরা এটিকে আরও কিছুটা স্পষ্ট করতে পারি যে আমাদের কাছে একটি অ্যাসিঙ্ক ইভেন্টহ্যান্ডলার রয়েছে যা ব্যতিক্রমগুলিকে বুদবুদ করা থেকে নিরাপদ হওয়া উচিত।
এই পদ্ধতিটি ইভেন্ট হ্যান্ডলারের বাইরে চলা থেকে সমস্যাযুক্ত কোডকে সময়ের সাথে সাথে কোড ড্রিফ্টকে প্রতিরোধ করবে। যাইহোক, এটি আপনাকে ম্যানুয়ালি এটি যোগ করার জন্য মনে রাখতে হবে এমন বিষয়টির সমাধান করবে না!
আসুন কোডটি পরীক্ষা করে দেখি:
static class EventHandlers { public static EventHandler<TArgs> TryAsync<TArgs>( Func<object, TArgs, Task> callback, Action<Exception> errorHandler) where TArgs : EventArgs => TryAsync<TArgs>( callback, ex => { errorHandler.Invoke(ex); return Task.CompletedTask; }); public static EventHandler<TArgs> TryAsync<TArgs>( Func<object, TArgs, Task> callback, Func<Exception, Task> errorHandler) where TArgs : EventArgs { return new EventHandler<TArgs>(async (object s, TArgs e) => { try { await callback.Invoke(s, e); } catch (Exception ex) { await errorHandler.Invoke(ex); } }); } }
উপরের কোডটি বেশ আক্ষরিক অর্থেই অ্যাসিঙ্ক অকার্যকর সীমানা অতিক্রম করা থেকে ব্যতিক্রম প্রতিরোধের জন্য একই পদ্ধতি ব্যবহার করে। আমরা কেবল ইভেন্ট হ্যান্ডলারের শরীরের চারপাশে ধরার চেষ্টা করি, কিন্তু এখন আমরা এটিকে পুনঃব্যবহারের জন্য একটি সুস্পষ্টভাবে উত্সর্গীকৃত পদ্ধতিতে বান্ডিল করেছি।
এটি প্রয়োগ করতে এটি দেখতে কেমন হবে তা এখানে:
someEventRaisingObject.TheEvent += EventHandlers.TryAsync<EventArgs>( async (s, e) => { Console.WriteLine("Starting the event handler..."); await SomeTaskToAwait(); Console.WriteLine("Event handler completed."); }, ex => Console.WriteLine($"[TryAsync Error Callback] Our exception handler caught: {ex}"));
আমরা দেখতে পাচ্ছি যে আমাদের কাছে এখন কাজ করার জন্য একটি async টাস্ক স্বাক্ষর সহ একজন প্রতিনিধি আছে, এবং আমরা যা কিছু রেখেছি তা আমরা নিশ্চিত করেছি যে সাহায্যকারী পদ্ধতিতে আমরা আগে দেখেছি তার চারপাশে চেষ্টা/ক্যাচ করা হবে।
এখানে ত্রুটি হ্যান্ডলার কলব্যাক সঠিকভাবে ব্যতিক্রম ক্যাপচার দেখানো একটি স্ক্রিনশট:
সুবিধা:
অসুবিধা:
মূলত আমি অন্বেষণ করতে সেট আউট
এই নিবন্ধে, আমরা অন্বেষণ করেছি যে আমি যুক্তি দিতে পারি তা হল আপনার অ্যাসিঙ্ক ইভেন্টহ্যান্ডলারদের সঠিকভাবে আচরণ করার সবচেয়ে সহজ উপায়, এবং পরিমার্জিত সমাধানে (আমার মতে) শুধুমাত্র সেই ত্রুটি রয়েছে যা আপনাকে এটি ব্যবহার করার জন্য মনে রাখতে হবে।
একজন মন্তব্যকারী পরামর্শ দিয়েছিলেন যে কেউ অন্বেষণ করতে পারে
কিছু কম্পাইল-টাইম AoP ফ্রেমওয়ার্ক রয়েছে যা বিদ্যমান, তবে আমি পাঠক হিসাবে এটি আপনার জন্য একটি অনুশীলন হিসাবে রেখে দেব (কারণ এটি আমার পক্ষে অনুসরণ করাও একটি অনুশীলন)।