ลอง / catch + using, ไวยากรณ์ที่ถูกต้อง


189

อันไหน:

using (var myObject = new MyClass())
{
   try
   {
      // something here...
   }
   catch(Exception ex)
   {
      // Handle exception
   }
}

หรือ

try
{
   using (var myObject = new MyClass())
   {
      // something here...
   }
}
catch(Exception ex)
{
   // Handle exception
}

7
เพียงทราบ: หนึ่งควรระวังที่จะจับเฉพาะข้อยกเว้นที่สามารถจัดการได้จริง(แก้ไข) ยกเว้นการเข้าสู่ระบบหรือห่อพวกเขา
John Saunders

1
กรุณาเก็บไว้ในใจว่ายังสุดท้าย}ของusingคำสั่งที่สามารถโยนข้อยกเว้นเป็นนึกถึงที่นี่
Giulio Caccin

1
TIL ที่ดีบักเกอร์ (ใน VS) จะไม่เรียกวิธีการกำจัดหากคุณใช้บล็อกแรกของรหัส เนื่องจากคำสั่งการใช้งานเองสามารถส่งข้อยกเว้นมันช่วยให้ฉันใช้บล็อกที่สองเพื่อให้แน่ใจfinallyว่ามีการใช้วิธีการกำจัด
ShooShoSha

คำตอบ:


98

ฉันชอบอันที่สอง อาจดักจับข้อผิดพลาดที่เกี่ยวข้องกับการสร้างวัตถุเช่นกัน


11
ฉันไม่เห็นด้วยกับคำแนะนำนี้ หากคุณคาดว่าการสร้างวัตถุจะเกิดข้อผิดพลาดการจัดการข้อยกเว้นนั้นจะต้องออกไปข้างนอก หากมีคำถามบางอย่างเกี่ยวกับการจัดการควรจะไปที่ไหนข้อยกเว้นที่คาดว่าจะต้องเป็นอย่างอื่น - เว้นแต่คุณจะสนับสนุนการจับข้อยกเว้นแบบสุ่มใด ๆ ที่อาจหรือไม่อาจคาดหวังซึ่งเป็นรูปแบบการต่อต้านแบบคลาสสิก กระบวนการหรือตัวจัดการข้อยกเว้น Unhandled ของเธรด)
Jeffrey L Whitledge

1
@ Jeffrey: วิธีที่ฉันอธิบายได้ทำหน้าที่ฉันได้ดีและฉันได้ทำมานานแล้ว ไม่มีใครพูดอะไรเกี่ยวกับการคาดหวังว่าการสร้างวัตถุจะล้มเหลว แต่โดยการตัดการดำเนินงานที่อาจอาจล้มเหลวในtryบล็อกซึ่งช่วยให้คุณที่จะปรากฏข้อความแสดงข้อผิดพลาดหากสิ่งที่ล้มเหลวโปรแกรมในขณะนี้มีความสามารถในการกู้คืนและแจ้งให้ผู้ใช้
โจนาธานวู้ด

คำตอบของคุณถูกต้อง แต่ยังคงคำแนะนำว่าลอง / จับมีจะมี (ทันที) ทุกครั้ง
Henk Holterman

17
ฉันคิดว่าอันแรกมีข้อดีเช่นกันพิจารณาธุรกรรม DB using( DBConnection conn = DBFactory.getConnection())ซึ่งจะต้องย้อนกลับในกรณีที่มีข้อยกเว้นเกิดขึ้น ดูเหมือนว่าฉันมีทั้งที่ของพวกเขา
wfoster

1
ที่จะดักข้อผิดพลาดที่เกี่ยวข้องกับการกำจัดวัตถุ
Ahmad Ibrahim

39

เนื่องจากบล็อกการใช้เป็นเพียงการทำให้เข้าใจง่ายขึ้นของลอง / ในที่สุด ( MSDN ), ส่วนตัวฉันจะไปกับต่อไปนี้ แต่ฉันสงสัยว่ามันแตกต่างจากตัวเลือกที่สองของคุณอย่างมีนัยสำคัญ:

MyClass myObject = null;
try {
  myObject = new MyClass();
  //important stuff
} catch (Exception ex) {
  //handle exception
} finally {
  if(myObject is IDisposable) myObject.Dispose();
}

4
ทำไมคุณคิดว่าการเพิ่มfinallyบล็อกเป็นที่ต้องการในusingคำสั่ง
Cody Gray

10
การเพิ่มfinallyบล็อกที่เลิกใช้วัตถุ IDisposable คือสิ่งที่usingคำสั่งทำ โดยส่วนตัวฉันชอบสิ่งนี้แทนที่จะเป็นusingบล็อกแบบฝังเพราะฉันคิดว่ามันสะอาดกว่าทุกสิ่งที่เกิดขึ้นและมันอยู่ในระดับเดียวกัน ฉันชอบแบบนี้มากกว่าusingบล็อกแบบฝังหลายอัน... แต่ทั้งหมดนี้เป็นเพียงความชอบของฉัน
chezy525

8
หากคุณใช้การจัดการข้อยกเว้นมากคุณต้องสนุกกับการพิมพ์จริงๆ! คำหลัก "ใช้" นั้นมีมาระยะหนึ่งแล้วและความหมายของมันค่อนข้างชัดเจนสำหรับฉัน และการใช้มันช่วยทำให้ส่วนที่เหลือของรหัสของฉันชัดเจนขึ้นโดยทำให้จำนวนของความยุ่งเหยิงให้น้อยที่สุด
โจนาธานวู้ด

2
สิ่งนี้ไม่ถูกต้อง วัตถุจะต้องถูกยกตัวอย่างนอกtryคำสั่งเพื่อให้มันถูกกำจัดภายในfinallyคำสั่ง; มิฉะนั้นจะส่งข้อผิดพลาดคอมไพเลอร์: "การใช้ตัวแปรท้องถิ่นที่ไม่ได้กำหนด 'myObject'"
Steve Konves

3
ในทางเทคนิคแล้วมันจะไม่คอมไพล์ด้วยเช่นกัน Cannot assign null to implicitly-typed local variable;) แต่ฉันรู้ว่าคุณหมายถึงอะไรและโดยส่วนตัวแล้วชอบที่จะทำรังบล็อก
คอนเนล

20

มันขึ้นอยู่กับ. หากคุณใช้ Windows Communication Foundation (WCF) using(...) { try... }จะไม่ทำงานอย่างถูกต้องหากพร็อกซีในusingคำสั่งอยู่ในสถานะข้อยกเว้นนั่นคือการทิ้งพร็อกซีนี้จะทำให้เกิดข้อยกเว้นอื่น

โดยส่วนตัวแล้วฉันเชื่อในวิธีการจัดการที่น้อยที่สุดนั่นคือจัดการกับข้อยกเว้นเพียงอย่างเดียวที่คุณทราบ ณ ตอนที่ทำการประหารชีวิต ในคำอื่น ๆ ถ้าคุณรู้ว่าการเริ่มต้นของในตัวแปรอาจจะโยนข้อยกเว้นเฉพาะอย่างยิ่งผมห่อมันด้วยusing try-catchในทำนองเดียวกันถ้าภายในusingร่างกายมีบางสิ่งบางอย่างที่อาจเกิดขึ้นซึ่งไม่เกี่ยวข้องกับตัวแปรโดยตรงusingฉันจะหุ้มมันด้วยอีกtryข้อยกเว้นนั้น ฉันไม่ค่อยได้ใช้Exceptionในcatches ของฉัน

แต่ฉันชอบIDisposableและusingแม้ว่าฉันอาจจะลำเอียง


19

หากคำสั่ง catch ของคุณต้องการเข้าถึงตัวแปรที่ประกาศใน statement โดยใช้คำสั่ง inside เป็นตัวเลือกเดียวของคุณ

หากข้อความสั่งการ catch ของคุณต้องการวัตถุที่อ้างอิงในการใช้งานก่อนที่จะถูกกำจัดแล้วภายในเป็นตัวเลือกเดียวของคุณ

หากคำสั่ง catch ของคุณดำเนินการในช่วงเวลาที่ไม่รู้จักเช่นการแสดงข้อความถึงผู้ใช้และคุณต้องการที่จะกำจัดทรัพยากรของคุณก่อนที่จะเกิดขึ้นนั่นคือตัวเลือกที่ดีที่สุดของคุณ

เมื่อใดก็ตามที่ฉันมี scenerio คล้ายกับนี้บล็อก try-catch มักจะอยู่ในวิธีการที่แตกต่างกันต่อไปขึ้นสแตกการโทรจากการใช้ มันไม่ใช่เรื่องปกติสำหรับวิธีที่จะรู้วิธีจัดการกับข้อยกเว้นที่เกิดขึ้นภายในแบบนี้

ดังนั้นคำแนะนำทั่วไปของฉันจึงอยู่ข้างนอก - ข้างนอก

private void saveButton_Click(object sender, EventArgs args)
{
    try
    {
        SaveFile(myFile); // The using statement will appear somewhere in here.
    }
    catch (IOException ex)
    {
        MessageBox.Show(ex.Message);
    }
}

10

ทั้งคู่เป็นไวยากรณ์ที่ถูกต้อง มันมาถึงสิ่งที่คุณต้องการทำจริง ๆ : ถ้าคุณต้องการตรวจจับข้อผิดพลาดที่เกี่ยวข้องกับการสร้าง / กำจัดวัตถุให้ใช้สิ่งที่สอง ถ้าไม่ใช้ให้ใช้ก่อน


8

มีสิ่งสำคัญสิ่งหนึ่งที่ฉันจะเรียกออกมาที่นี่: สิ่งแรกที่จะไม่จับข้อยกเว้นใด ๆ ที่เกิดขึ้นจากการเรียกตัวMyClassสร้าง


3

จาก C # 8.0 ฉันชอบใช้อันที่สองแบบนี้

public class Person : IDisposable
{
    public Person()
    {
        int a = 0;
        int b = Id / a;
    }
    public int Id { get; set; }

    public void Dispose()
    {
    }
}

แล้ว

static void Main(string[] args)
    {

        try
        {
            using var person = new Person();
        }
        catch (Exception ex) when
        (ex.TargetSite.DeclaringType.Name == nameof(Person) &&
        ex.TargetSite.MemberType == System.Reflection.MemberTypes.Constructor)
        {
            Debug.Write("Error Constructor Person");
        }
        catch (Exception ex) when
       (ex.TargetSite.DeclaringType.Name == nameof(Person) &&
       ex.TargetSite.MemberType != System.Reflection.MemberTypes.Constructor)
        {
            Debug.Write("Error Person");
        }
        catch (Exception ex)
        {
            Debug.Write(ex.Message);
        }
        finally
        {
            Debug.Write("finally");
        }
    }

1

หากวัตถุที่คุณกำลังเริ่มต้นในบล็อก Using () อาจมีข้อยกเว้นใด ๆ คุณควรไปที่ไวยากรณ์ที่สองมิฉะนั้นทั้งสองจะใช้ได้อย่างเท่าเทียมกัน

ในสถานการณ์ของฉันฉันต้องเปิดไฟล์และฉันกำลังส่ง filePath ในตัวสร้างของวัตถุที่ฉันเริ่มต้นในบล็อก Using () และมันอาจส่งข้อยกเว้นถ้า filePath ผิด / ว่างเปล่า ดังนั้นในกรณีนี้ไวยากรณ์ที่สองจึงสมเหตุสมผล

รหัสตัวอย่างของฉัน: -

try
{
    using (var obj= new MyClass("fileName.extension"))
    {

    }
}
catch(Exception ex)
{
     //Take actions according to the exception.
}

1

จากC # 8.0 บนคุณสามารถทำให้usingคำสั่งง่ายขึ้นภายใต้เงื่อนไขบางอย่างเพื่อกำจัดบล็อกที่ซ้อนกันและจากนั้นจะใช้กับบล็อกที่ล้อมรอบ

ดังนั้นตัวอย่างสองตัวอย่างของคุณสามารถลดลงเป็น:

using var myObject = new MyClass();
try
{
   // something here...
}
catch(Exception ex)
{
   // Handle exception
}

และ:

try
{
   using var myObject = new MyClass();
   // something here...
}
catch(Exception ex)
{
   // Handle exception
}

ซึ่งทั้งคู่ค่อนข้างชัดเจน และจากนั้นจะลดตัวเลือกระหว่างสองสิ่งเข้ากับสิ่งที่คุณต้องการให้ขอบเขตของวัตถุอยู่ในตำแหน่งที่คุณต้องการจัดการข้อผิดพลาดการเริ่มต้นและเมื่อคุณต้องการกำจัดมัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.