รอคอยในการสกัดกั้น


85

ฉันมีรหัสต่อไปนี้:

WebClient wc = new WebClient();
string result;
try
{
  result = await wc.DownloadStringTaskAsync( new Uri( "http://badurl" ) );
}
catch
{
  result = await wc.DownloadStringTaskAsync( new Uri( "http://fallbackurl" ) );
}

โดยทั่วไปฉันต้องการดาวน์โหลดจาก URL และเมื่อล้มเหลวโดยมีข้อยกเว้นฉันต้องการดาวน์โหลดจาก URL อื่น ทั้งสองเวลาไม่ตรงกันแน่นอน อย่างไรก็ตามรหัสไม่ได้รวบรวมเนื่องจาก

ข้อผิดพลาด CS1985: ไม่สามารถรอในเนื้อหาของประโยค catch

ตกลงห้ามไม่ว่าด้วยเหตุผลใด แต่รูปแบบรหัสที่ถูกต้องนี่คืออะไร

แก้ไข:

ข่าวดีก็คือว่าC # 6.0 มีแนวโน้มที่จะช่วยให้สายรอคอยทั้งในและในที่สุดก็จับบล็อก

คำตอบ:


103

อัปเดต: C # 6.0 รองรับการรอคอย


คำตอบเก่า : คุณสามารถเขียนโค้ดนั้นใหม่เพื่อย้ายawaitจากcatchบล็อกโดยใช้แฟล็ก:

WebClient wc = new WebClient();
string result = null;
bool downloadSucceeded;
try
{
  result = await wc.DownloadStringTaskAsync( new Uri( "http://badurl" ) );
  downloadSucceeded = true;
}
catch
{
  downloadSucceeded = false;
}

if (!downloadSucceeded)
  result = await wc.DownloadStringTaskAsync( new Uri( "http://fallbackurl" ) );

7
ขอบคุณ svick ที่ค่อนข้างชัดเจนมีอะไรดีขึ้นเชื่อมต่อกับ async มากขึ้น?
GyörgyBalássy

ฉันไม่คิดว่าจะมีอะไรแบบนั้น
svick

3
ในกรณีของคุณคุณสามารถใช้งานต่อเนื่องได้ แต่รหัสในsvickคำตอบนั้นสะอาดกว่ารหัสที่ใช้การต่อเนื่อง
Stephen Cleary

16
หากคุณจำเป็นต้องลบข้อยกเว้นอีกครั้งโดยไม่สูญเสีย callstack คุณยังสามารถใช้ System.Runtime.ExceptionServices.ExceptionDispatchInfoคลาสแบบคงที่ได้ เพียงแค่เรียกใช้ExceptionDispatchInfo.Capture(ex)catch block ของคุณและจัดเก็บค่าที่ส่งคืนข้อยกเว้นที่จับได้ในตัวแปรท้องถิ่น เมื่อคุณใช้รหัส async เสร็จแล้วคุณสามารถใช้capturedException.Throw()ซึ่งจะลบข้อยกเว้นเดิมได้อย่างถูกต้อง
Etienne Maheu

เทคนิคที่ยอดเยี่ยม
Zia Ur Rahman

24

ขณะนี้การรอในบล็อกจับเป็นไปได้ในการดูตัวอย่างผู้ใช้ปลายทางของ Roslyn ดังที่แสดงไว้ที่นี่ (อยู่ภายใต้ Await in catch / ในที่สุด)และจะรวมอยู่ใน C # 6

ตัวอย่างที่ระบุคือ

try … catch { await … } finally { await … }

อัปเดต:เพิ่มลิงค์ที่ใหม่กว่าและจะอยู่ใน C # 6


9

ดูเหมือนว่าจะได้ผล

        WebClient wc = new WebClient();
        string result;
        Task<string> downloadTask = wc.DownloadStringTaskAsync(new Uri("http://badurl"));
        downloadTask = downloadTask.ContinueWith(
            t => {
                return wc.DownloadStringTaskAsync(new Uri("http://google.com/")).Result;
            }, TaskContinuationOptions.OnlyOnFaulted);
        result = await downloadTask;

6

ลองดูสิ:

         try
        {
            await AsyncFunction(...);
        }

        catch(Exception ex)
        { 
            Utilities.LogExceptionToFile(ex).Wait();
            //instead of "await Utilities.LogExceptionToFile(ex);"
        }

(ดูWait()ตอนจบ)



1

รูปแบบที่ฉันใช้เพื่อลบข้อยกเว้นใหม่หลังจากรอภารกิจสำรอง:

ExceptionDispatchInfo capturedException = null;
try
{
  await SomeWork();
}
catch (Exception e)
{
  capturedException = ExceptionDispatchInfo.Capture(e);
}

if (capturedException != null)
{
  await FallbackWork();
  capturedException.Throw();
}

1

คุณสามารถใช้นิพจน์แลมบ์ดาได้ดังนี้:

  try
    {
        //.....
    }
    catch (Exception ex)
    {
        Action<Exception> lambda;

        lambda = async (x) =>
        {
            // await (...);
        };

        lambda(ex);
    }

สิ่งนี้ทำให้แลมด้าasync voidซึ่งไม่ควรใช้เว้นแต่คุณจะต้องทำ
svick


0

ในกรณีที่คล้ายกันฉันไม่สามารถรอในด่านจับได้ อย่างไรก็ตามฉันสามารถตั้งค่าสถานะและใช้แฟล็กในคำสั่ง if (โค้ดด้านล่าง)

---------------------------------------...

boolean exceptionFlag = false; 

try 
{ 
do your thing 
} 
catch 
{ 
exceptionFlag = true; 
} 

if(exceptionFlag == true){ 
do what you wanted to do in the catch block 
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.