เราจะทราบได้อย่างไรว่าการอ้างอิงวัตถุที่ระบุได้ถูกกำจัดทิ้งไป?


88

มีวิธีการหรือวิธีอื่นที่มีน้ำหนักเบาในการตรวจสอบว่ามีการอ้างอิงถึงวัตถุที่กำจัดทิ้งหรือไม่?

PS - นี่เป็นเพียงความอยากรู้อยากเห็น (หลับให้สบายไม่ใช่รหัสการผลิต) ใช่ฉันรู้ว่าฉันสามารถจับได้ObjectDisposedExceptionเมื่อพยายามเข้าถึงสมาชิกของวัตถุ


11
Dunno. ดูเหมือนว่าอยากรู้อยากเห็นว่ามีไม่ได้เป็นประกาศบนbool IsDisposed { get; } System.IDisposable
nicodemus 13

3
@ nicodemus13: Disposeวิธีนี้สั่งให้วัตถุปล่อยทรัพยากรใด ๆ และทั้งหมดที่ได้มา แต่ยังไม่ได้รับการปลดปล่อย ถ้าออบเจ็กต์ไม่เคยเก็บทรัพยากรDisposeโดยทั่วไปวิธีการของมันจะไม่ต้องทำอะไรเลย หากประเภทประกาศvoid IDisposable.Dispose() {};ก็สามารถละเว้นIDisposableโดยไม่มีค่าใช้จ่ายต่ออินสแตนซ์ IsDisposedทรัพย์สินซึ่งคาดว่าจะเป็นความจริงใด ๆ ต่อไปนี้โทรจะเลี่ยงการเพิ่มสถานะบูลีนมิฉะนั้นไม่จำเป็นตัวอย่างของหลายชนิดที่อาจจะไม่สนใจทุกDispose Dispose
supercat

1
แต่เมื่อใดก็ตามที่คุณเรียกใช้เมธอดบนวัตถุที่ใช้งานIDisposableคุณจะตรวจสอบได้อย่างไรว่าถูกกำจัดก่อนหรือไม่? แทนที่จะคิดว่ามันไม่ใช่และจับข้อยกเว้น? หรืออย่างใดคุณตั้งใจที่จะจัดการอายุการใช้งานเพื่อที่คุณจะได้รู้ว่ามันถูกกำจัดหรือไม่?
nicodemus13

3
@ nicodemus13: โดยทั่วไปไม่ควรใช้วัตถุโดยไม่รู้ว่ายังไม่ได้รับและจะไม่ถูกกำจัดยกเว้นในกรณีที่มีการเตรียมพิจารณาการกำจัดวัตถุโดยใช้รหัสภายนอกเป็นสัญญาณในการยกเลิกการดำเนินการใด ๆ ที่รอดำเนินการกับมัน . IsDisposedธงอาจช่วยป้องกันรหัสจากการสูญเสียเวลาในการดำเนินงานที่ไม่อาจประสบความสำเร็จ แต่ยังคงต้องจัดการกับข้อยกเว้นในกรณีที่วัตถุได้รับการกำจัดระหว่างIsDisposedการตรวจสอบและความพยายามที่จะใช้มัน
supercat

WeakReferenceดูเหมือนจะเกี่ยวข้องที่นี่ ไม่ใช่เครื่องตรวจจับ IDipose'd อย่างแน่นอน แต่มันบอกคุณได้ว่าเป็น GC หรือไม่
Malachi

คำตอบ:



42

System.Windows.Forms.ControlมียอดIsDisposedสถานที่ให้บริการซึ่งเป็นที่ตั้งหลังจากที่จริงDispose()จะเรียกว่า ในอ็อบเจ็กต์ IDisposable ของคุณเองคุณสามารถสร้างคุณสมบัติที่คล้ายกันได้อย่างง่ายดาย


OP ต้องการดูว่ามีคุณสมบัติที่คล้ายกันอยู่แล้วบนวัตถุที่เขาไม่ได้สร้างหรือไม่ นี่เป็นความคิดที่ดีสำหรับออบเจ็กต์ที่เราสร้างขึ้น แต่คลาสที่ใช้แล้วทิ้งส่วนใหญ่ใน. NET ไม่เป็นไปตามแบบแผนนี้ คำตอบของ Dandikas ถูกต้อง
krillgar

2
@krillgar ไม่มีอะไรในคำถามของ OP ที่สนับสนุนการยืนยันของคุณ
Ryan Lundy

19

ไม่มีสิ่งใดในตัวที่จะยอมให้สิ่งนี้ คุณจะต้องแสดงคุณสมบัติบูลีน IsDisposed ที่สะท้อนแฟล็กที่กำจัดภายใน

public class SimpleCleanup : IDisposable
{
    private bool disposed = false;

    public bool IsDisposed
    {
       get
       {
          return disposed;
       }
    }

    public SimpleCleanup()
    {
        this.handle = /*...*/;
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
               // free only managed resources here
            }

            // free unmanaged resources here
            disposed = true;
        }
    }

    public void Dispose()
    {
        Dispose(true);
    }
}

BTW ถ้าใครเริ่มใช้รูปแบบนี้จะช่วยกำหนดอินเทอร์เฟซใหม่ ( IDisposablePlusหรืออะไรก็ได้) ที่สืบทอดมาจากIDisposableและรวมbool IsDisposed { get; }ไว้ด้วย ซึ่งทำให้มันเป็นเรื่องง่ายที่จะได้รู้ว่าที่ที่คุณสนับสนุนวัตถุIDisposable IsDisposed
ToolmakerSteve

ฉันไม่คิดว่าคุณจะสืบทอดอินเทอร์เฟซได้เนื่องจาก C # ทำงานอย่างไร การวางอินเทอร์เฟซหลังจากโคลอนสืบทอดมัน ฉันคาดหวังว่ามันจะใช้อินเทอร์เฟซที่อื่น
โมเสส

9

ถ้าไม่ใช่คลาสของคุณและไม่มีคุณสมบัติ IsDisposed (หรืออะไรที่คล้ายกัน - ชื่อนี้เป็นเพียงแบบแผน) คุณก็ไม่มีทางรู้ได้

แต่ถ้าเป็นชั้นเรียนของคุณและคุณกำลังติดตามการนำไปใช้งานตามมาตรฐานที่กำหนดได้ก็เพียงแค่แสดงฟิลด์ _disposed หรือ _isDisposed เป็นคุณสมบัติและตรวจสอบสิ่งนั้น


2

Disposeวิธีการที่จำเป็นในการดำเนินการทำความสะอาดสิ่งที่จะต้องก่อนที่วัตถุถูกยกเลิก; หากไม่จำเป็นต้องล้างข้อมูลก็ไม่จำเป็นต้องทำอะไร การกำหนดให้ออบเจ็กต์ติดตามว่าวัตถุนั้นถูกกำจัดไปแล้วหรือไม่แม้ว่าDisposeเมธอดจะไม่ทำอะไรเลยก็ตามจะต้องใช้IDisposableอ็อบเจ็กต์จำนวนมากเพื่อเพิ่มแฟล็กเพื่อประโยชน์ที่ จำกัด มาก

อาจมีประโยชน์หากIDisposableมีคุณสมบัติสองอย่าง - คุณสมบัติหนึ่งที่ระบุว่าวัตถุจำเป็นต้องกำจัดหรือไม่และหนึ่งในนั้นระบุว่าวัตถุไม่ได้ถูกทำให้ไร้ประโยชน์จากการกำจัด Disposeสำหรับวัตถุที่กำจัดไม่จริงสิ่งที่ทั้งสองค่าจะเป็นจริงในตอนแรกและจะกลายเป็นเท็จหลัง สำหรับวัตถุที่การกำจัดไม่จำเป็นต้องทำการล้างข้อมูลใด ๆ วิธีแรกอาจส่งคืนเท็จเสมอและวิธีที่สองเป็นจริงเสมอโดยไม่ต้องเก็บแฟล็กไว้ที่ใดก็ได้ ฉันไม่คิดว่าจะมีวิธีใดที่สามารถเพิ่มลงใน. NET ได้ในตอนนี้


IMHO สองแฟล็กเกินความจำเป็น ฉันคิดว่ามันจะดีกว่าที่จะยึดติดกับกระบวนทัศน์ปกติโดยที่หนึ่งมีแฟล็กเดียวเมื่อ Dispose ถูกเรียกใช้กับอ็อบเจ็กต์ มิฉะนั้นคุณจะเพิ่มความซับซ้อนเพียงเพื่อให้ทราบว่าวัตถุบางอย่าง "ยังคงมีประโยชน์" แม้ว่าจะมีการเรียก Dispose ไปก็ตาม มันไม่คุ้มที่จะไปตามถนนนั้น
ToolmakerSteve

@ToolmakerSteve: โดยทั่วไปจะมีค่าสถานะเป็นศูนย์หรือหนึ่งรายการ สำหรับวัตถุที่ต้องการการกำจัดคุณสมบัติ "ต้องกำจัด" และ "เป็นประโยชน์" จะให้ผลเป็น "จริง / จริง" ก่อนที่จะกำจัดและ "เท็จ / เท็จ" ในภายหลัง แต่สำหรับวัตถุที่การกำจัดจะเป็นแบบไม่ต้องดำเนินการทั้งสองอย่างจะไม่มีเงื่อนไข ส่งคืน "เท็จ / จริง" การบอกว่าวัตถุยังคงต้องการการกำจัดเมื่อมันไม่เคยทำหรือว่าวัตถุไม่มีประโยชน์เมื่อมันอยู่เสมอจะค่อนข้างน่าเบื่อ ฉันคิดว่าอีกวิธีหนึ่งคือการใช้ประเภทที่แจกแจงเพื่อระบุว่าประเภทนั้นต้องการการกำจัดถูกกำจัดหรือไม่สนใจ
supercat

@ToolmakerSteve: ฉันคิดว่าเหตุผลใหญ่ที่IDisposableไม่มีDisposedคุณสมบัติก็คือมันจะถูกมองว่าแปลกที่มีวัตถุที่การโทรDisposeไม่ได้ตั้งค่าคุณสมบัติดังกล่าวtrueแต่ต้องการให้วัตถุนั้นติดตามว่าDisposeถูกเรียกในกรณีที่ มิฉะนั้นพวกเขาจะไม่มีเหตุผลที่จะดูแลจะเพิ่มต้นทุนที่สำคัญและผลประโยชน์เพียงเล็กน้อย
supercat

1

ฉันเห็นว่ามันเก่า แต่ฉันไม่เห็นคำตอบ อ็อบเจ็กต์ที่ใช้แล้วทิ้งบางอย่างเช่น DataSet อาจมีเหตุการณ์ที่ถูกทิ้งที่คุณสามารถแนบ

class DisposeSample : IDisposable
{
    DataSet myDataSet = new DataSet();
    private bool _isDisposed;

    public DisposeSample()
    {
        // attach dispose event for myDataSet
        myDataSet.Disposed += MyDataSet_Disposed;
    }

    private void MyDataSet_Disposed(object sender, EventArgs e)
    {
        //Event triggers when myDataSet is disposed
        _isDisposed = true; // set private bool variable as true 
    }


    public void Dispose()
    {
        if (!_isDisposed) // only dispose if has not been disposed;
            myDataSet?.Dispose(); // only dispose if myDataSet is not null;
    }
}

ดีแล้วที่รู้. โดยเฉพาะDisposedเหตุการณ์เป็นสมาชิกของSystem.ComponentModel.IComponentอินเทอร์เฟซ
ToolmakerSteve

-1

สิ่งที่ฉันต้องการจะทำคือประกาศวัตถุโดยไม่ต้องเริ่มต้นพวกเขา Nothingแต่ตั้งค่าเริ่มต้นของพวกเขาไป จากนั้นในตอนท้ายของลูปฉันเขียน:

If anObject IsNot Nothing Then anObject.Dispose()

นี่คือตัวอย่างที่สมบูรณ์:

Public Sub Example()
    Dim inputPdf As PdfReader = Nothing, inputDoc As Document = Nothing, outputWriter As PdfWriter = Nothing

    'code goes here that may or may not end up using all three objects, 
    ' such as when I see that there aren't enough pages in the pdf once I open  
    ' the pdfreader and then abort by jumping to my cleanup routine using a goto ..

GoodExit:
    If inputPdf IsNot Nothing Then inputPdf.Dispose()
    If inputDoc IsNot Nothing Then inputDoc.Dispose()
    If outputWriter IsNot Nothing Then outputWriter.Dispose()
End Sub

วิธีนี้ยังใช้งานได้ดีในการวางวัตถุหลักไว้ที่ด้านบนของกิจวัตรโดยใช้สิ่งเหล่านี้ภายในTryกิจวัตรแล้วทิ้งในFinallyบล็อก

Private Sub Test()
    Dim aForm As System.Windows.Forms.Form = Nothing
    Try
        Dim sName As String = aForm.Name  'null ref should occur
    Catch ex As Exception
        'got null exception, no doubt
    Finally
        'proper disposal occurs, error or no error, initialized or not..
        If aForm IsNot Nothing Then aForm.Dispose()
    End Try
End Sub

6
@ LarsHöppner: สาระสำคัญของคำถามคือไม่เชื่อเรื่องพระเจ้าภาษาและนักพัฒนา C # ที่ดีควรรู้จัก VB.NET อย่างน้อยเพียงพอที่จะอ่านโค้ดด้านบน (และนักพัฒนา VB.NET ก็ควรเรียนรู้ C # มากพอที่จะอ่านรหัส C # ที่ไม่ได้ ทำอะไรที่แปลกใหม่เป็นพิเศษ)
supercat

3
ทำไมคุณถึงทำทั้งหมดนี้แทนที่จะใช้Usingคำสั่ง? มีอยู่อย่างแน่นอนในปี 2013 เมื่อคำตอบนี้ถูกเขียนขึ้น
โคดี้เกรย์

"GoodExit:" ปี 1983 สำหรับ GOTO คืออะไร ?? โปรดหยุดใช้สิ่งนั้น
โมเสส

สิ่งนี้ไม่ตอบคำถาม โดยเฉพาะเมื่อinputPdfถูกตั้งค่าเป็นค่า (นอกเหนือจาก Nothing) คำตอบของคุณจะแสดงให้เห็นว่าไม่มีทางรู้ว่าinputPdfถูกกำจัดไปหรือไม่ คุณสามารถแก้ไขบางส่วนได้โดยการตั้งค่าinputPdf = Nothingหลังจากกำจัดทิ้ง อย่างไรก็ตามสิ่งนี้จะไม่ช่วยให้ตัวแปรอื่นใดที่ถูกชี้ไปที่วัตถุเดียวกันinputPdfด้วย นั่นคือถ้าคุณทำ: inputPdf = New PdfReader, Dim pdf2 As PdfReader = inputPdf, inputPdf.Dispose, inputPdf = Nothingก็จะยังคงเป็นวิธีที่จะรู้ว่าไม่pdf2เป็นที่จำหน่าย (มันเป็นวัตถุเช่นเดียวinputPdf)
ToolmakerSteve
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.