"Phép trừ ủy quyền có kết quả không thể đoán trước" trong ReSharper / C #?


124

Khi sử dụng myDelegate -= eventHandlerReSharper (phiên bản 6), các vấn đề:

Phép trừ ủy nhiệm có kết quả không thể đoán trước

Lý do đằng sau điều này được giải thích bởi JetBrains ở đây . Lời giải thích có lý và, sau khi đọc nó, tôi nghi ngờ tất cả những cách sử dụng của tôi đối -với các đại biểu.

Làm thế nào sau đó ,

  • tôi có thể viết một sự kiện không phải ô tô mà không làm cho ReSharper khó chịu không?
  • hoặc, có cách nào tốt hơn và / hoặc "đúng" để thực hiện điều này không?
  • hoặc, tôi có thể bỏ qua ReSharper không?

Đây là mã đơn giản:

public delegate void MyHandler (object sender);

MyHandler _myEvent;

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

Mono đưa ra cảnh báo tương tự. Dưới đây là R # 's mô tả về các vấn đề confluence.jetbrains.com/display/ReSharper/... (mà chỉ áp dụng cho danh sách các đại biểu)
thoredge

Câu trả lời:


134

Đừng sợ! Phần đầu tiên của cảnh báo ReSharper chỉ áp dụng cho việc xóa danh sách các đại biểu. Trong mã của bạn, bạn luôn xóa một đại biểu duy nhất. Phần thứ hai nói về thứ tự của các đại biểu sau khi một đại biểu trùng lặp đã bị loại bỏ. Một sự kiện không đảm bảo thứ tự thực hiện cho những người đăng ký của nó, vì vậy nó cũng không thực sự ảnh hưởng đến bạn.

Vì cơ chế trên có thể dẫn đến kết quả không thể đoán trước, ReSharper đưa ra cảnh báo bất cứ khi nào nó gặp toán tử trừ đại biểu.

ReSharper đang đưa ra cảnh báo này vì phép trừ đại biểu đa hướng có thể có lỗi, nó không hoàn toàn lên án tính năng ngôn ngữ đó. May mắn thay, những gotchas đó nằm trong các trường hợp rìa và bạn khó có thể gặp phải nếu bạn chỉ là công cụ cho các sự kiện đơn giản. Không có cách nào tốt hơn để triển khai các trình xử lý add/ của riêng removebạn, bạn chỉ cần chú ý.

Tôi khuyên bạn nên hạ cấp cảnh báo của ReSharper cho thông báo đó thành "Gợi ý" để bạn không bị nhạy cảm với cảnh báo của họ, điều này thường hữu ích.


66
Tôi nghĩ rằng thật tệ của R # khi gọi kết quả là "không thể đoán trước". Chúng được chỉ định rất rõ ràng. "Không phải những gì người dùng có thể dự đoán" không giống với "không thể đoán trước" theo bất kỳ cách nào. (Nó cũng không chính xác khi nói rằng định nghĩa NET framework quá tải -. Nó nướng vào biên dịch C # Delegatekhông không quá tải +-.)
Jon Skeet

6
@Jon: Tôi đồng ý. Tôi đoán mọi người đã quen với tiêu chuẩn cao mà Microsoft đặt ra cho mình. Mức độ đánh bóng ngày càng cao cùng với quá nhiều thứ trong thế giới .NET khiến bạn “rơi vào hố sâu thành công”, gặp phải đặc điểm ngôn ngữ chỉ là bước đi nhanh bên cạnh hố sâu nơi có cơ hội bạn có thể bỏ lỡ nó được một số người coi là chói tai và đảm bảo một dấu hiệu nói PIT OF SUCCESS IS THAT WAY --->.
Allon Guralnek

5
@AllonGuralnek: Mặt khác, lần cuối cùng bạn nghe nói về ai đó thực sự gặp sự cố do điều này là khi nào?
Jon Skeet

5
@Jon: Nghe nói có vấn đề với nó? Tôi thậm chí còn không biết về hành vi này trước khi câu hỏi này được đăng.
Allon Guralnek

2
Thật tò mò rằng R # sẽ cảnh báo về phép trừ đại biểu, nhưng không cảnh báo về các triển khai phổ biến của các sự kiện có cùng vấn đề . Vấn đề cốt lõi là .net sử dụng một Delegate.Combineđại biểu duy nhất "làm phẳng" các đại biểu đa hướng, vì vậy nếu nó được cung cấp các đại biểu [X, Y] và Z, nó không thể biết liệu kết quả nên là [X, Y, Z] hay [[ X, Y], Z] (đại biểu thứ hai giữ [X,Y]đại biểu làm đại diện TargetInvokephương thức của đại biểu đó làm phương thức của nó Method).
supercat

28

Bạn không nên sử dụng trực tiếp các đại biểu để tính tổng hoặc trừ. Thay vào đó lĩnh vực của bạn

MyHandler _myEvent;

Thay vào đó, cũng nên được khai báo như một sự kiện. Điều này sẽ giải quyết vấn đề mà không gây rủi ro cho giải pháp của bạn và vẫn có lợi cho việc sử dụng sự kiện.

event MyHandler _myEvent;

Việc sử dụng tổng hoặc trừ đại biểu là nguy hiểm vì bạn có thể mất các sự kiện khi chỉ cần chỉ định đại biểu (theo khai báo, nhà phát triển sẽ không trực tiếp suy ra đây là đại biểu Multicast như khi nó được khai báo là một sự kiện). Để minh họa, nếu thuộc tính được đề cập trong câu hỏi này không được gắn cờ là một sự kiện, thì đoạn mã dưới đây sẽ đặt trường hợp hai nhiệm vụ đầu tiên bị LOST, bởi vì ai đó chỉ đơn giản là gán cho người được ủy quyền (điều này cũng hợp lệ!).

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

Khi gán Method3, tôi hoàn toàn mất hai đăng ký ban đầu. Sử dụng sự kiện sẽ tránh được sự cố này và đồng thời loại bỏ cảnh báo ReSharper.


Tôi chưa bao giờ nghĩ đến việc làm theo cách này, nhưng nó có ý nghĩa khi bảo vệ việc sử dụng đại biểu sự kiện miễn là bạn giữ sự kiện cơ bản đó ở chế độ riêng tư. Tuy nhiên, điều này không hoạt động tốt khi có đồng bộ hóa chuỗi cụ thể hơn phải xảy ra trong trình xử lý thêm / xóa, chẳng hạn như nhiều đăng ký sự kiện hoặc đăng ký phụ cũng cần được theo dõi. Tuy nhiên, trong mọi trường hợp, nó loại bỏ các cảnh báo.
Jeremy

-17

đặt nó thành = null thay vì sử dụng - =


5
các removephương pháp của một sự kiện không nên loại bỏ tất cả các bộ xử lý, nhưng thay vì xử lý đã được yêu cầu phải được loại bỏ.
Servy

nếu anh ta chỉ thêm một cái thì nếu anh ta chỉ xóa một cái có hiệu lực, anh ta sẽ xóa tất cả. Tôi không nói rằng hãy sử dụng nó trong mọi trường hợp chỉ cho thông báo trình sạc lại cụ thể này.
Jonathan Beresford

6
Nhưng bạn không biết rằng ủy quyền luôn xóa mục duy nhất trong danh sách lệnh gọi. Điều này làm cho thông báo trình nạp lại biến mất bằng cách biến mã hoạt động chính xác thành mã hỏng không chính xác, trong một số trường hợp, sẽ ngẫu nhiên hoạt động, nhưng điều đó sẽ bị hỏng theo những cách bất thường và khó chẩn đoán trong nhiều trường hợp.
Servy

Tôi đã phải ủng hộ điều này vì -17 là một hình phạt quá khắc nghiệt đối với một người nào đó dành thời gian để viết một câu trả lời "tiềm năng". +1 để không bị động, ngay cả khi bạn không chính xác.
John C
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.