“ การลบตัวแทนมีผลลัพธ์ที่คาดเดาไม่ได้” ใน ReSharper / C #?


124

เมื่อใช้ปัญหาmyDelegate -= eventHandlerReSharper (เวอร์ชัน 6):

การลบผู้รับมอบสิทธิ์มีผลลัพธ์ที่คาดเดาไม่ได้

เหตุผลที่อยู่เบื้องหลังนี้คือการอธิบายโดย JetBrains ที่นี่ คำอธิบายมีเหตุผลและหลังจากอ่านแล้วฉันสงสัยว่าการใช้งานทั้งหมดของฉัน-กับผู้ได้รับมอบหมาย

วิธีแล้ว ,

  • ฉันสามารถเขียนเหตุการณ์ที่ไม่ใช่อัตโนมัติโดยไม่ทำให้ ReSharper ไม่พอใจได้หรือไม่?
  • หรือมีวิธีที่ดีกว่าและ / หรือ "ถูกต้อง" ในการนำไปใช้หรือไม่?
  • หรือฉันสามารถเพิกเฉยต่อ ReSharper ได้หรือไม่?

นี่คือรหัสแบบง่าย:

public delegate void MyHandler (object sender);

MyHandler _myEvent;

public event MyHandler MyEvent
{
    add
    {
        _myEvent += value;
        DoSomethingElse();
    }
    remove
    {
        _myEvent -= value; // <-- ReSharper warning here
    }
}

โมโนก็เตือนเหมือนกัน นี่คือคำอธิบายของ R # เกี่ยวกับปัญหาconfluence.jetbrains.com/display/ReSharper/… (ซึ่งใช้กับรายชื่อผู้ได้รับมอบหมายเท่านั้น)
thoredge

คำตอบ:


134

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

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

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

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


67
ฉันคิดว่า R # ไม่ดีที่จะเรียกผลลัพธ์ว่า "คาดเดาไม่ได้" มีการระบุไว้ชัดเจนมาก "ไม่ใช่สิ่งที่ผู้ใช้คาดเดาได้" ไม่เหมือนกับ "คาดเดาไม่ได้" แต่อย่างใด (นอกจากนี้ยังเป็นที่ไม่ถูกต้องที่จะกล่าวว่ากรอบ NET กำหนด overloads -. ก็อบเป็นคอมไพเลอร์ C # Delegateจะไม่เกิน+และ-.)
จอน Skeet

6
@ จอน: ฉันเห็นด้วย ฉันเดาว่าทุกคนคงเคยชินกับการตั้งค่าระดับสูงของไมโครซอฟต์ ระดับการขัดเกลาที่สูงพอ ๆ กับหลายสิ่งหลายอย่างในโลก. NET ที่ทำให้คุณ "ตกอยู่ในหลุมแห่งความสำเร็จ" โดยต้องพบกับคุณลักษณะทางภาษาที่เป็นเพียงการเดินเร็ว ๆ ถัดจากหลุมซึ่งมี PIT OF SUCCESS IS THAT WAY --->โอกาสที่คุณอาจจะพลาดก็คือการพิจารณาโดยบางอย่างที่จะสั่นสะเทือนและใบสำคัญแสดงสิทธิเป็นสัญญาณบอกว่า
Allon Guralnek

5
@AllonGuralnek: ในทางกลับกันครั้งสุดท้ายที่คุณได้ยินว่ามีคนมีปัญหาเกิดขึ้นจริงคือเมื่อไหร่?
Jon Skeet

5
@ จอน: ได้ยินว่ามีปัญหาหรือไม่? ฉันไม่รู้เกี่ยวกับพฤติกรรมนี้ก่อนที่จะโพสต์คำถามนี้
Allon Guralnek

2
มันอยากรู้อยากเห็นว่า R # จะเตือนเกี่ยวกับผู้รับมอบสิทธิ์การลบ แต่ไม่ได้เตือนเกี่ยวกับการใช้งานร่วมกันของเหตุการณ์ที่มีปัญหาเดียวกันแน่นอน ปัญหาหลักคือ. net ใช้Delegate.Combineตัวแทนแบบมัลติคาสต์ที่ "แบน" ดังนั้นหากได้รับมอบหมาย [X, Y] และ Z จะไม่สามารถบอกได้ว่าผลลัพธ์ควรเป็น [X, Y, Z] หรือ [[ X, Y], Z] (ผู้รับมอบสิทธิ์คนหลังถือ[X,Y]ผู้รับมอบสิทธิ์เป็นของตนTargetและInvokeวิธีการของผู้แทนนั้นเป็นของตนMethod)
supercat

28

คุณไม่ควรใช้ผู้รับมอบสิทธิ์โดยตรงเพื่อรวมหรือลบ แทนสนามของคุณ

MyHandler _myEvent;

ควรประกาศแทนเหตุการณ์เช่นกัน วิธีนี้จะแก้ปัญหาได้โดยไม่ต้องเสี่ยงกับโซลูชันของคุณและยังมีประโยชน์จากการใช้งานเหตุการณ์

event MyHandler _myEvent;

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

myObject.MyEvent += Method1; 
myObject.MyEvent += Method2;
myObject.MyEvent = Method3;

เมื่อกำหนด Method3 ฉันสูญเสียการสมัครสมาชิกเริ่มต้นสองรายการโดยสิ้นเชิง การใช้งานเหตุการณ์จะหลีกเลี่ยงปัญหานี้และในขณะเดียวกันก็ลบคำเตือน ReSharper


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

-17

ตั้งค่าเป็น = null แทนที่จะใช้ - =


5
removeวิธีการของเหตุการณ์ที่ไม่ควรเอารถขนทั้งหมด แต่การจัดการที่ถูกขอให้นำออก
Servy

ถ้าเขาเพิ่มเพียงอันเดียวถ้าเขาลบออกเพียงอันเดียวเขาก็ลบออกทั้งหมด ฉันไม่ได้บอกว่าใช้มันในทุกกรณีสำหรับข้อความ resharper เฉพาะนี้
Jonathan Beresford

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

ฉันต้องโหวตให้คะแนนนี้เพราะ -17 เป็นการลงโทษที่รุนแรงเกินไปสำหรับคนที่สละเวลาเขียนคำตอบที่ "มีศักยภาพ" +1 เพื่อไม่ให้อยู่เฉยๆแม้ว่าคุณจะพูดผิดก็ตาม
John C
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.