คุณจะป้องกัน IDisposable จากการแพร่กระจายไปยังชั้นเรียนทั้งหมดของคุณได้อย่างไร


138

เริ่มด้วยคลาสที่เรียบง่ายเหล่านี้ ...

สมมติว่าฉันมีชุดเรียนที่เรียบง่ายเช่นนี้:

class Bus
{
    Driver busDriver = new Driver();
}

class Driver
{
    Shoe[] shoes = { new Shoe(), new Shoe() };
}

class Shoe
{
    Shoelace lace = new Shoelace();
}

class Shoelace
{
    bool tied = false;
}

BusมีDriverที่DriverมีสองShoes แต่ละมีShoe Shoelaceทั้งหมดโง่มาก

เพิ่มวัตถุ IDisposable ใน Shoelace

ต่อมาฉันตัดสินใจว่าการดำเนินการบางอย่างเกี่ยวกับShoelaceอาจเป็นหลายเธรดดังนั้นฉันจึงเพิ่มEventWaitHandleสำหรับเธรดที่จะสื่อสารด้วย ดังนั้นShoelaceตอนนี้ดูเหมือนว่านี้:

class Shoelace
{
    private AutoResetEvent waitHandle = new AutoResetEvent(false);
    bool tied = false;
    // ... other stuff ..
}

ติดตั้ง IDisposable บนเชือกผูกรองเท้า

แต่ตอนนี้FxCop ของ Microsoftจะบ่นว่า: "Implement IDisposable ใน 'Shoelace' เพราะมันจะสร้างสมาชิกประเภท IDisposable ต่อไปนี้: 'EventWaitHandle'

โอเคฉันใช้IDisposableต่อไปShoelaceและชั้นเรียนเล็ก ๆ ที่เรียบร้อยของฉันจะกลายเป็นความยุ่งเหยิงที่น่ากลัว:

class Shoelace : IDisposable
{
    private AutoResetEvent waitHandle = new AutoResetEvent(false);
    bool tied = false;
    private bool disposed = false;

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    ~Shoelace()
    {
        Dispose(false);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                if (waitHandle != null)
                {
                    waitHandle.Close();
                    waitHandle = null;
                }
            }
            // No unmanaged resources to release otherwise they'd go here.
        }
        disposed = true;
    }
}

หรือ (ตามที่ผู้วิจารณ์ให้ความเห็น) เนื่องจากShoelaceตัวมันเองไม่มีทรัพยากรที่ไม่มีการจัดการฉันอาจใช้การจัดการที่ง่ายกว่าโดยไม่จำเป็นต้องใช้Dispose(bool)และ Destructor:

class Shoelace : IDisposable
{
    private AutoResetEvent waitHandle = new AutoResetEvent(false);
    bool tied = false;

    public void Dispose()
    {
        if (waitHandle != null)
        {
            waitHandle.Close();
            waitHandle = null;
        }
        GC.SuppressFinalize(this);
    }
}

ดูในหนังสยองขวัญในขณะที่ IDisposablek แพร่กระจาย

ใช่แล้วนั่นคือการแก้ไข แต่ตอนนี้ FxCop จะบ่นว่าShoeสร้าง a Shoelaceดังนั้นShoeต้องเป็นIDisposableเช่นกัน

และDriverสร้างShoeจึงต้องDriver IDisposableและBusสร้างDriverดังนั้นBusจะต้องเป็นIDisposableและอื่น ๆ

ทันใดนั้นการเปลี่ยนแปลงขนาดเล็กของฉันที่จะShoelaceเป็นสาเหตุของผมมากในการทำงานและเจ้านายของฉันจะสงสัยว่าทำไมฉันต้องเช็คเอาท์เพื่อให้การเปลี่ยนแปลงไปBusShoelace

คำถาม

คุณจะป้องกันการแพร่กระจายนี้ได้IDisposableอย่างไร แต่ยังมั่นใจได้ว่าวัตถุที่ไม่มีการจัดการของคุณถูกกำจัดอย่างเหมาะสม


9
เป็นคำถามที่ดีเป็นพิเศษฉันเชื่อว่าคำตอบคือลดการใช้งานและพยายามรักษา IDisposables ระดับสูงในระยะสั้น ๆ ด้วยการใช้งาน แต่มันไม่สามารถทำได้เสมอไป ดู fwd กับคำตอบ
annakata

โอเคแดนฉันได้อัปเดตคำถามเพื่อแสดงวิธีการใช้ IDisposable ทั้งสองบน Shoelace
GrahamS

ฉันมักจะระวังการพึ่งพารายละเอียดการใช้งานของชั้นเรียนอื่นเพื่อปกป้องฉัน ไม่มีจุดเสี่ยงหากฉันสามารถป้องกันได้อย่างง่ายดาย บางทีฉันอาจจะมากกว่าระมัดระวังหรือบางทีฉันใช้เวลานานเกินไปเป็นโปรแกรมเมอร์ C แต่ฉันอยากจะใช้วิธีการไอริช: "เพื่อให้แน่ใจว่าเพื่อให้แน่ใจว่า" :)
แกรห์ม

@ ด่าน: การตรวจสอบโมฆะยังจำเป็นเพื่อให้แน่ใจว่าวัตถุนั้นไม่ได้ถูกตั้งค่าเป็นโมฆะในกรณีนี้การเรียกไปยัง waitHandle.Dispose () จะเป็นการโยน NullReferenceException
46490 Scott

1
ไม่ว่าจะเกิดอะไรขึ้นคุณควรใช้เมธอด Dispose (bool) ดังที่แสดงในส่วน "Implement IDisposable บน Shoelace" เนื่องจากว่า (ลบด้วย finalizer) เป็นรูปแบบเต็ม เพียงเพราะคลาส implants IDisposable ไม่ได้หมายความว่ามันต้องการ finalizer
34490 Scott

คำตอบ:


36

คุณไม่สามารถ "ป้องกัน" IDisposable จากการแพร่กระจาย บางคลาสจำเป็นต้องกำจัดเช่นAutoResetEventและวิธีที่มีประสิทธิภาพมากที่สุดคือการทำในDispose()วิธีการเพื่อหลีกเลี่ยงค่าใช้จ่ายของ finalizers แต่วิธีการนี้จะต้องเรียกว่าอย่างใดดังนั้นในตัวอย่างของคุณคลาสที่แค็ปซูลหรือมี IDisposable ต้องกำจัดสิ่งเหล่านี้ดังนั้นพวกเขาจะต้องถูกทิ้งเช่นกัน ฯลฯ วิธีเดียวที่จะหลีกเลี่ยงคือ:

  • หลีกเลี่ยงการใช้คลาส IDisposable หากเป็นไปได้ล็อคหรือรอเหตุการณ์ในที่เดียวเก็บทรัพยากรราคาแพงไว้ในที่เดียว ฯลฯ
  • สร้างพวกเขาเฉพาะเมื่อคุณต้องการพวกเขาและทิ้งพวกเขาในภายหลัง ( usingรูปแบบ)

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


4
คำแนะนำที่คุณสามารถเพิกเฉยได้เพราะการนำไปปฏิบัติในวันนี้ไม่ได้ทำอะไรที่ไม่ดีเพราะเวอร์ชั่นในอนาคตอาจทำสิ่งที่สำคัญในการกำจัดและตอนนี้คุณก็ยากที่จะติดตามการรั่วไหล
Andy

1
"รักษาทรัพยากรที่มีราคาแพง" IDisposable นั้นไม่จำเป็นต้องมีราคาแพงแน่นอนตัวอย่างนี้ซึ่งใช้ตัวจัดการการรอแม้แต่ประมาณ "เบา" ที่คุณได้รับ
markmnl

20

ในแง่ของความถูกต้องคุณไม่สามารถป้องกันการแพร่กระจายของ IDisposable ผ่านความสัมพันธ์ของวัตถุถ้าวัตถุหลักสร้างและเป็นเจ้าของวัตถุลูกซึ่งจะต้องถูกทิ้ง FxCop ถูกต้องในสถานการณ์นี้และพาเรนต์ต้องเป็น IDisposable

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

หากคุณสามารถย้าย WaitHandle ไปสู่การเชื่อมโยงที่หลวมผ่านแผนที่ ณ จุดที่ WaitHandle ใช้งานจริงคุณสามารถแบ่งสายโซ่นี้ได้


2
รู้สึกแปลกมากที่ได้สร้างสมาคมที่นั่น AutoResetEvent นี้เป็นส่วนตัวของการนำ Shoelace มาใช้ดังนั้นการเปิดเผยต่อสาธารณะจะผิดในความคิดของฉัน
GrahamS

@ GrahamS ฉันไม่ได้พูดอย่างเปิดเผย ฉันกำลังพูดว่าสามารถย้ายไปยังจุดที่ผูกเชือกผูกรองเท้าได้ บางทีถ้าการผูกเชือกผูกรองเท้านั้นเป็นงานที่สลับซับซ้อนควรจะเป็นชั้นระดับเชือกผูกรองเท้า
JaredPar

1
คุณช่วยขยายคำตอบของคุณด้วยตัวอย่างโค้ด JaredPar ได้หรือไม่? ฉันอาจจะยืดตัวอย่างของฉันเล็กน้อย แต่ฉันจินตนาการว่าเชือกผูกรองเท้าสร้างและเริ่มด้าย Tyer ซึ่งรออย่างอดทนที่ waitHandle.WaitOne () เชือกผูกรองเท้าจะเรียก waitHandle.Set () เมื่อต้องการให้ด้ายเริ่มผูก
GrahamS

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

16

เพื่อป้องกันIDisposableการแพร่กระจายคุณควรพยายามห่อหุ้มการใช้วัตถุที่ใช้แล้วทิ้งภายในวิธีการเดียว ลองออกแบบให้Shoelaceแตกต่างกัน:

class Shoelace { 
  bool tied = false; 

  public void Tie() {

    using (var waitHandle = new AutoResetEvent(false)) {

      // you can even pass the disposable to other methods
      OtherMethod(waitHandle);

      // or hold it in a field (but FxCop will complain that your class is not disposable),
      // as long as you take control of its lifecycle
      _waitHandle = waitHandle;
      OtherMethodThatUsesTheWaitHandleFromTheField();

    } 

  }
} 

ขอบเขตของการจัดการการรอคอย จำกัด อยู่ที่Tieวิธีการและคลาสไม่จำเป็นต้องมีฟิลด์แบบใช้ครั้งเดียวและดังนั้นจึงไม่จำเป็นต้องถูกทิ้ง

เนื่องจากหมายเลขอ้างอิงการรอคอยเป็นรายละเอียดการนำไปปฏิบัติด้านในShoelaceจึงไม่ควรเปลี่ยนแปลงอินเทอร์เฟซสาธารณะใด ๆ เช่นเพิ่มอินเทอร์เฟซใหม่ในการประกาศ จะเกิดอะไรขึ้นเมื่อคุณไม่ต้องการฟิลด์ที่ใช้แล้วทิ้งอีกต่อไปคุณจะลบการIDisposableประกาศหรือไม่ หากคุณคิดเกี่ยวกับShoelace สิ่งที่เป็นนามธรรม , IDisposableคุณรู้อย่างรวดเร็วว่ามันไม่ควรจะถูกปนเปื้อนด้วยการพึ่งพาโครงสร้างพื้นฐานเช่น IDisposableควรสงวนไว้สำหรับคลาสที่ abstraction encapsulate ทรัพยากรที่เรียกใช้เพื่อล้างค่าที่กำหนดขึ้น กล่าวคือสำหรับการเรียนที่ใช้ได้ตามความเป็นส่วนหนึ่งของสิ่งที่เป็นนามธรรม


1
ฉันเห็นด้วยอย่างสมบูรณ์ว่ารายละเอียดการดำเนินการของ Shoelace ไม่ควรทำให้เกิดความเสียหายต่อส่วนต่อประสานสาธารณะ แต่ประเด็นของฉันคือการหลีกเลี่ยงได้ยาก สิ่งที่คุณแนะนำที่นี่อาจเป็นไปไม่ได้เสมอไป: วัตถุประสงค์ของ AutoResetEvent () คือการสื่อสารระหว่างเธรดดังนั้นขอบเขตมักจะขยายออกไปมากกว่าวิธีการเดียว
GrahamS

@ GrahamS: นั่นเป็นเหตุผลที่ฉันบอกว่าพยายามออกแบบมันในแบบนั้น นอกจากนี้คุณยังสามารถส่งทิ้งไปยังวิธีการอื่น ๆ มันจะหยุดพักเมื่อสายภายนอกไปที่คลาสควบคุมวงจรชีวิตของทิ้ง ฉันจะอัปเดตคำตอบตามนั้น
Jordão

2
ขออภัยฉันรู้ว่าคุณสามารถผ่านรอบที่ใช้แล้วทิ้ง แต่ฉันยังไม่เห็นว่าการทำงาน ในตัวอย่างของฉันAutoResetEventใช้เพื่อสื่อสารระหว่างเธรดต่าง ๆ ที่ทำงานภายในคลาสเดียวกันดังนั้นจึงต้องเป็นตัวแปรสมาชิก คุณไม่สามารถ จำกัด ขอบเขตให้กับวิธีใดวิธีหนึ่ง (เช่นลองคิดดูว่าหนึ่งเธรดกำลังรองานบางอย่างโดยการปิดกั้นwaitHandle.WaitOne()จากนั้นเธรดหลักจะเรียกshoelace.Tie()เมธอดซึ่งทำเพียง a waitHandle.Set()และส่งคืนทันที)
GrahamS

3

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

ต้องบอกว่าคุณสามารถตัดรูปแบบทิ้งได้อย่างมากเมื่อคุณไม่มีทรัพยากรที่ไม่มีการจัดการ (ฉันยังคงมองหาข้อมูลอ้างอิงอย่างเป็นทางการสำหรับเรื่องนี้)

แต่คุณสามารถละเว้น destructor และ GC.SuppressFinalize (นี่); และอาจจะล้างข้อมูล void Dispose (bool disposing) เล็กน้อย


ขอบคุณ Henk ถ้าฉันนำการสร้างรอมือจับออกมาจากเชือกผูกรองเท้าแล้วมีคนยังคงต้องกำจัดมันที่ไหนสักแห่งเพื่อให้มีคนรู้ว่าเชือกผูกรองเท้าเสร็จกับมัน (และ Shoelace ไม่ได้ส่งไปเรียนอื่น ๆ )
GrahamS

คุณจะต้องย้ายไม่เพียง แต่การสร้าง แต่ยังเป็น 'ความรับผิดชอบสำหรับ' waithandle คำตอบของ jaredPar มีจุดเริ่มต้นที่น่าสนใจ
Henk Holterman

บล็อกนี้ครอบคลุมถึงการใช้ IDisposable และโดยเฉพาะอย่างยิ่งที่อยู่วิธีการทำให้งานง่ายขึ้นโดยหลีกเลี่ยงการผสมทรัพยากรที่มีการจัดการและไม่มีการจัดการในบล็อก
PaulS

3

ที่น่าสนใจหากDriverมีการกำหนดไว้ข้างต้น:

class Driver
{
    Shoe[] shoes = { new Shoe(), new Shoe() };
}

จากนั้นเมื่อShoeสร้างเสร็จIDisposableแล้ว FxCop (v1.36) ก็ไม่Driverควรบ่นเช่นIDisposableกัน

อย่างไรก็ตามถ้ามันถูกกำหนดเช่นนี้:

class Driver
{
    Shoe leftShoe = new Shoe();
    Shoe rightShoe = new Shoe();
}

แล้วมันจะบ่น

ฉันสงสัยว่านี่เป็นเพียงข้อ จำกัด ของ FxCop แทนที่จะเป็นวิธีแก้ปัญหาเพราะในรุ่นแรกShoeอินสแตนซ์ยังคงถูกสร้างโดยDriverและยังคงต้องถูกกำจัดอย่างใด


2
หาก Show ใช้ IDisposable การสร้างใน initializer ฟิลด์จะเป็นอันตราย น่าสนใจที่ FXCop จะไม่อนุญาตให้เริ่มต้นเช่นนั้นเนื่องจากใน C # วิธีเดียวที่จะทำได้อย่างปลอดภัยคือกับตัวแปร ThreadStatic (น่าเกลียดอย่างสิ้นเชิง) ใน vb.net เป็นไปได้ที่จะเริ่มต้นวัตถุ IDisposable อย่างปลอดภัยโดยไม่ต้องตัวแปร ThreadStatic รหัสยังคงน่าเกลียด แต่ไม่ค่อยน่าเกลียดนัก ฉันหวังว่า MS จะเป็นวิธีที่ดีในการใช้ initializers ภาคสนามอย่างปลอดภัย
supercat

1
@supercat: ขอบคุณ คุณช่วยอธิบายได้ไหมว่าทำไมมันอันตราย ฉันเดาว่าถ้ามีข้อยกเว้นถูกโยนลงในหนึ่งในตัวShoeสร้างแล้วshoesจะถูกทิ้งให้อยู่ในสถานะที่ยากลำบาก?
GrahamS

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

3
วิธีเดียวที่ฉันรู้ที่จะจัดการกับสิ่งนั้นใน C # คือการใช้ตัวแปร ThreadStatic เพื่อเก็บรายการของวัตถุที่จะต้องกำจัดหากคอนสตรัคเตอร์ปัจจุบันโยน จากนั้นให้แต่ละเขตข้อมูล initializer ลงทะเบียนแต่ละวัตถุในขณะที่มันถูกสร้างขึ้นให้โรงงานล้างรายการถ้ามันเสร็จสมบูรณ์และใช้ประโยค "ในที่สุด" เพื่อกำจัดรายการทั้งหมดจากรายการถ้ามันไม่ได้ล้าง นั่นจะเป็นวิธีที่ใช้การได้ แต่ตัวแปร ThreadStatic น่าเกลียด ใน vb.net เราสามารถใช้เทคนิคที่คล้ายกันได้โดยไม่ต้องใช้ตัวแปร ThreadStatic ใด ๆ
supercat

3

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

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

การออกแบบทางเลือกอาจเป็น:

class Bus
{
   IDriver busDriver = null;
   public void SetDriver(IDriver d) { busDriver = d; }
}

class Driver : IDriver
{
   IShoePair shoes = null;
   public void PutShoesOn(IShoePair p) { shoes = p; }
}

class ShoePairWithDisposableLaces : IShoePair, IDisposable
{
   Shoelace lace = new Shoelace();
}

class Shoelace : IDisposable
{
   ...
}

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


2
สิ่งนี้ไม่ได้ช่วยแก้ปัญหาดั้งเดิมได้ มันอาจทำให้ FxCop เงียบ แต่มีบางสิ่งที่ควรกำจัด Shoelace
AndrewS

ฉันคิดว่าสิ่งที่คุณทำไปแล้วเป็นปัญหาที่อื่น ShoePairWithDisposableLaces ตอนนี้เป็นเจ้าของ Shoelace () ดังนั้นจึงต้องทำเป็น IDisposable - ใครเป็นผู้จำหน่ายรองเท้า? คุณทิ้งสิ่งนี้ไว้ในคอนเทนเนอร์ IoC เพื่อจัดการใช่ไหม
GrahamS

@AndrewS: มีบางสิ่งที่ยังต้องกำจัดไดรเวอร์รองเท้าและเชือกผูกรองเท้า แต่การกำจัดไม่ควรเริ่มต้นด้วยรถบัส @ GrahamS: ถูกต้องฉันกำลังเคลื่อนปัญหาไปที่อื่นเพราะฉันเชื่อว่ามันเป็นของที่อื่น โดยทั่วไปจะมีรองเท้าอินสแตนซ์ชั้นเรียนซึ่งจะต้องรับผิดชอบในการกำจัดของรองเท้า ฉันได้แก้ไขรหัสเพื่อให้ ShoePairWithDisposableLaces ใช้แล้วทิ้งเช่นกัน
Joh

@Joh: ขอบคุณ อย่างที่คุณพูดปัญหาเกี่ยวกับการย้ายที่อื่นคือคุณสร้างความซับซ้อนมากขึ้น หาก ShoePairWithDisposableLaces ถูกสร้างขึ้นโดยคลาสอื่นบางคลาสดังนั้นจะไม่ต้องแจ้งคลาสนั้นเมื่อไดร์เวอร์เสร็จสิ้นด้วยรองเท้าของเขาเพื่อที่จะสามารถกำจัด () อย่างถูกต้องหรือไม่
GrahamS

1
@ Graham: ใช่จำเป็นต้องมีกลไกการแจ้งเตือนบางประเภท นโยบายการกำจัดตามจำนวนการอ้างอิงสามารถนำมาใช้เช่น มีบางอย่างที่เพิ่มความซับซ้อน แต่ในขณะที่คุณอาจรู้ :) "ไม่มีสิ่งดังกล่าวเป็นรองเท้าฟรี"
โจ้

1

วิธีการเกี่ยวกับการใช้ Inversion of Control?

class Bus
{
    private Driver busDriver;

    public Bus(Driver busDriver)
    {
        this.busDriver = busDriver;
    }
}

class Driver
{
    private Shoe[] shoes;

    public Driver(Shoe[] shoes)
    {
        this.shoes = shoes;
    }
}

class Shoe
{
    private Shoelace lace;

    public Shoe(Shoelace lace)
    {
        this.lace = lace;
    }
}

class Shoelace
{
    bool tied;
    private AutoResetEvent waitHandle;

    public Shoelace(bool tied, AutoResetEvent waitHandle)
    {
        this.tied = tied;
        this.waitHandle = waitHandle;
    }
}

class Program
{
    static void Main(string[] args)
    {
        using (var leftShoeWaitHandle = new AutoResetEvent(false))
        using (var rightShoeWaitHandle = new AutoResetEvent(false))
        {
            var bus = new Bus(new Driver(new[] {new Shoe(new Shoelace(false, leftShoeWaitHandle)),new Shoe(new Shoelace(false, rightShoeWaitHandle))}));
        }
    }
}

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

@andy คุณหมายถึงอะไร "Shoelace ไม่สามารถพึ่งพาการจัดการกับการรอเมื่อมันต้องการ"? มันผ่านเข้าไปในตัวสร้าง
Darragh

มีบางอย่างอื่นที่อาจจะยุ่งกับสถานะของออโตเซทเทอเรเตอร์ก่อนที่จะมอบให้กับเชือกผูกรองเท้าและมันอาจเริ่มต้นด้วยการอยู่ในสถานะที่ไม่ดี; บางสิ่งบางอย่างอาจทำให้สกปรกด้วยสถานะของ ARE ในขณะที่เชือกผูกรองเท้ากำลังทำสิ่งนั้นทำให้เชือกผูกรองเท้าทำสิ่งที่ผิด มันเป็นเหตุผลเดียวกับที่คุณล็อคเฉพาะสมาชิกส่วนตัว "โดยทั่วไป .. หลีกเลี่ยงการล็อกอิน
Andy

ฉันเห็นด้วยกับการล็อคเฉพาะสมาชิกส่วนตัว แต่ดูเหมือนว่าการรอคอยจะแตกต่างกัน ในความเป็นจริงดูเหมือนว่ามีประโยชน์มากกว่าที่จะส่งพวกเขาไปยังวัตถุเช่นอินสแตนซ์เดียวกันจะต้องใช้ในการสื่อสารข้ามกระทู้ อย่างไรก็ตามฉันคิดว่า IoC มอบทางออกที่มีประโยชน์สำหรับปัญหาของ OP
Darragh

ระบุว่าตัวอย่าง OPs มี waithandle ในฐานะสมาชิกส่วนตัวสมมติฐานที่ปลอดภัยคือวัตถุคาดหวังการเข้าถึงแบบเอกสิทธิ์เฉพาะบุคคล การทำให้การจัดการสามารถมองเห็นได้นอกอินสแตนซ์ละเมิดนั้น โดยทั่วไปแล้ว IoC จะดีสำหรับสิ่งต่าง ๆ เช่นนี้ แต่ไม่ใช่เมื่อพูดถึงการทำเกลียว
แอนดี้
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.