চেষ্টা / ধরা / অবশেষে অপেক্ষা করার জন্য একটি ভাল সমাধান?


94

আমাকে আবার এই ব্যতিক্রমটি (তার স্ট্যাক ট্রেস সহ) নিক্ষেপ করার আগে asyncএকটি catchব্লকে একটি পদ্ধতি কল করতে হবে :

try
{
    // Do something
}
catch
{
    // <- Clean things here with async methods
    throw;
}

তবে দুর্ভাগ্যক্রমে আপনি awaitএকটি catchবা finallyব্লক ব্যবহার করতে পারবেন না । আমি শিখেছি কারণ সংকলকটির কোনও নির্দেশ নেই যা catchআপনার awaitনির্দেশের পরে বা এর মতো কিছু কার্যকর করার জন্য কোনও ব্লকে ফিরে যেতে পারে ...

আমি Task.Wait()প্রতিস্থাপনের জন্য ব্যবহার করার চেষ্টা করেছি awaitএবং আমি একটি অচলাবস্থা পেয়েছি। আমি কীভাবে এড়াতে পারি এবং এই সাইটটি পেয়েছি তা ওয়েবে অনুসন্ধান করেছি ।

যেহেতু আমি asyncপদ্ধতিগুলি পরিবর্তন করতে পারি না এবং তারা কী ব্যবহার করে তা আমি জানি না, তাই আমি ConfigureAwait(false)এই পদ্ধতিগুলি তৈরি করেছি যা Func<Task>একবার ভিন্ন থ্রেডে (অচলাবস্থা এড়াতে) আসার পরে অ্যাসিঙ্ক পদ্ধতি শুরু করে এবং এর সমাপ্তির জন্য অপেক্ষা করে:

public static void AwaitTaskSync(Func<Task> action)
{
    Task.Run(async () => await action().ConfigureAwait(false)).Wait();
}

public static TResult AwaitTaskSync<TResult>(Func<Task<TResult>> action)
{
    return Task.Run(async () => await action().ConfigureAwait(false)).Result;
}

public static void AwaitSync(Func<IAsyncAction> action)
{
    AwaitTaskSync(() => action().AsTask());
}

public static TResult AwaitSync<TResult>(Func<IAsyncOperation<TResult>> action)
{
    return AwaitTaskSync(() => action().AsTask());
}

সুতরাং আমার প্রশ্নগুলি: আপনি কি মনে করেন যে এই কোডটি ঠিক আছে?

অবশ্যই, যদি আপনার কিছু বর্ধিততা থাকে বা আরও ভাল পদ্ধতির বিষয়টি জানেন তবে আমি শুনছি! :)


4
awaitC # 6.0 থেকে প্রকৃতপক্ষে ক্যাচ ব্লকে ব্যবহার করার অনুমতি রয়েছে (নীচে আমার উত্তর দেখুন)
আদি লেস্টার

4
সম্পর্কিত সি # 5.0 ত্রুটি বার্তাগুলি: CS1985 : একটি ক্যাচ ক্লজের মূল অংশে অপেক্ষা করা যাবে না। CS1984 : অবশেষে একটি
ডেভিডআরআর

উত্তর:


174

আপনি যুক্তি বাহিরে স্থানান্তর করতে পারেন catchব্লক এবং, পরে ব্যতিক্রম rethrow যদি ব্যবহার করে, প্রয়োজন ExceptionDispatchInfo

static async Task f()
{
    ExceptionDispatchInfo capturedException = null;
    try
    {
        await TaskThatFails();
    }
    catch (MyException ex)
    {
        capturedException = ExceptionDispatchInfo.Capture(ex);
    }

    if (capturedException != null)
    {
        await ExceptionHandler();

        capturedException.Throw();
    }
}

এইভাবে, কলার যখন ব্যতিক্রমের StackTraceসম্পত্তিটি পরীক্ষা করে তখন TaskThatFailsএটি তার ভিতরে কোথায় ফেলেছিল তা এখনও রেকর্ড করে ।


4
(স্টিফেন ক্লিয়ারির উত্তরে) ExceptionDispatchInfoপরিবর্তে রাখার সুবিধা কী Exception?
ভারভারা কালিনিনা

4
আমি অনুমান করতে পারি এটি যদি আপনি পুনর্নির্মাণের সিদ্ধান্ত নেন তবে আপনি Exceptionআগের সমস্তটি হারাবেন StackTrace?
ভারভারা কালিনিনা

4
টুইটগুলি

54

আপনার জানা উচিত যে সি # 6.0 থেকে এটি ব্যবহার awaitকরা catchএবং finallyব্লক করা সম্ভব , সুতরাং আপনি বাস্তবে এটি করতে পারেন:

try
{
    // Do something
}
catch (Exception ex)
{
    await DoCleanupAsync();
    throw;
}

নতুন সি # 6.0 এক আমি শুধু উল্লেখ সহ বৈশিষ্ট্য, এখানে তালিকাভুক্ত করা হয় বা ভিডিও হিসাবে এখানে


সি # 6.0 এ ক্যাচ / অবশেষে ব্লকগুলির জন্য অপেক্ষা করার জন্য সমর্থন উইকিপিডিয়ায়ও নির্দেশিত।
ডেভিডআরআর

4
@ ডেভিডআর উইকিপিডিয়া অনুমোদিত নয়। এটি যতটা লক্ষ লক্ষ লক্ষ মানুষের মধ্যে এটি কেবলমাত্র অন্য একটি ওয়েবসাইট।
ব্যবহারকারী 34660

এটি সি # 6 এর জন্য বৈধ, প্রশ্নটি প্রথম থেকেই সি # 5 ট্যাগ ছিল। এই উত্তরটি এখানে বিভ্রান্তিকর হয় কিনা তা আমাদের বিস্মিত করে তোলে বা যদি আমাদের কেবলমাত্র এই ক্ষেত্রে নির্দিষ্ট সংস্করণ ট্যাগটি সরিয়ে ফেলা উচিত।
জুলাইলগন

16

আপনার যদি asyncত্রুটি হ্যান্ডলারগুলি ব্যবহার করার দরকার হয় তবে আমি এর মতো কিছু প্রস্তাব করব:

Exception exception = null;
try
{
  ...
}
catch (Exception ex)
{
  exception = ex;
}

if (exception != null)
{
  ...
}

asyncকোডটি সমকালীনভাবে ব্লক করা নিয়ে সমস্যা (এটি কোন থ্রেডে চলছে তা নির্বিশেষে) আপনি সিঙ্ক্রোনিকভাবে ব্লক করছেন। বেশিরভাগ পরিস্থিতিতে, এটি ব্যবহার করা ভাল await

আপডেট: যেহেতু আপনাকে পুনর্বিবেচনা করা দরকার তাই আপনি ব্যবহার করতে পারেন ExceptionDispatchInfo


4
আপনাকে ধন্যবাদ তবে দুর্ভাগ্যক্রমে আমি এই পদ্ধতিটি ইতিমধ্যে জানি। আমি সাধারনত এটাই করি তবে আমি এখানে এটি করতে পারি না। যদি আমি কেবল ব্যবহার throw exception;মধ্যে ifবিবৃতি স্ট্যাক ট্রেস হারিয়ে যাবে।
ব্যবহারকারী 2397050

3

আমরা আমাদের প্রকল্পে নিম্নলিখিত পুনরায় ব্যবহারযোগ্য ইউটিলিটি ক্লাসের জন্য এইচডিডির দুর্দান্ত উত্তরটি বের করেছি :

public static class TryWithAwaitInCatch
{
    public static async Task ExecuteAndHandleErrorAsync(Func<Task> actionAsync,
        Func<Exception, Task<bool>> errorHandlerAsync)
    {
        ExceptionDispatchInfo capturedException = null;
        try
        {
            await actionAsync().ConfigureAwait(false);
        }
        catch (Exception ex)
        {
            capturedException = ExceptionDispatchInfo.Capture(ex);
        }

        if (capturedException != null)
        {
            bool needsThrow = await errorHandlerAsync(capturedException.SourceException).ConfigureAwait(false);
            if (needsThrow)
            {
                capturedException.Throw();
            }
        }
    }
}

এটি নিম্নলিখিত হিসাবে ব্যবহার করা হবে:

    public async Task OnDoSomething()
    {
        await TryWithAwaitInCatch.ExecuteAndHandleErrorAsync(
            async () => await DoSomethingAsync(),
            async (ex) => { await ShowMessageAsync("Error: " + ex.Message); return false; }
        );
    }

নামটি উন্নত করতে নির্দ্বিধায়, আমরা এটিকে ইচ্ছাকৃতভাবে ভার্জোজ করে রেখেছি। নোট করুন যে ইতিমধ্যে কল সাইটে ক্যাপচার করা হয়েছে বলে মোড়কের ভিতরে প্রসঙ্গটি ক্যাপচার করার দরকার নেই ConfigureAwait(false)

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.