2009-08-07 11 views
30

Tôi chắc chắn rằng tôi chỉ không hiểu những điều căn bản về các sự kiện và/hoặc các đại biểu trong C#, nhưng tại sao tôi không thể làm các bài kiểm tra Boolean trong mẫu mã này:Trong C#, tại sao tôi không thể kiểm tra nếu một trình xử lý sự kiện là null ở bất kỳ đâu bên ngoài lớp mà nó được định nghĩa?

public class UseSomeEventBase { 
    public delegate void SomeEventHandler(object sender, EventArgs e); 
    public event SomeEventHandler SomeEvent; 
    protected void OnSomeEvent(EventArgs e) { 
     // CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS. 
     if (SomeEvent != null) SomeEvent(this, e); 
    } 
} 

public class UseSomeEvent : UseSomeEventBase { 
    public bool IsSomeEventHandlerNull() { 
     // "LEFT HAND SIDE" COMPILER ERROR 
     return SomeEvent == null; 
    } 
} 

class Program { 
    static void Main(string[] args) { 
     var useSomeEvent = new UseSomeEvent(); 
     useSomeEvent.SomeEvent +=new UseSomeEventBase.SomeEventHandler(FuncToHandle); 
     // "LEFT HAND SIDE" COMPILER ERROR 
     if (useSomeEvent.SomeEvent == null) { 

     } 
     var useSomeEventBase = new UseSomeEventBase(); 
     useSomeEventBase.SomeEvent += new UseSomeEventBase.SomeEventHandler(FuncToHandle); 
     // "LEFT HAND SIDE" COMPILER ERROR 
     if (useSomeEventBase.SomeEvent == null) { 

     } 
    } 

    static void FuncToHandle(object sender, EventArgs e) { } 
} 

Trả lời

55

Một sự kiện thực sự chỉ là một là "thêm" hoạt động và một "loại bỏ" hoạt động. Bạn không thể nhận giá trị, bạn không thể đặt giá trị, bạn không thể đặt giá trị - bạn chỉ có thể đăng ký trình xử lý cho sự kiện (add) hoặc hủy đăng ký (remove). Điều này là tốt - đó là đóng gói, đơn giản và đơn giản. Nhà xuất bản có thể triển khai thêm/xóa một cách thích hợp, trừ khi nhà xuất bản chọn cung cấp thông tin chi tiết, người đăng ký không thể sửa đổi hoặc truy cập các phần triển khai cụ thể.

sự kiện Dòng giống như trong C# (nơi bạn không chỉ định thêm/gỡ bỏ bit) ẩn này - họ tạo ra một biến của một loại đại biểu một sự kiện. Sự triển khai thêm/xóa của sự kiện chỉ sử dụng biến để theo dõi người đăng ký.

Bên trong lớp bạn tham khảo biến (để bạn có thể nhận được đại biểu đã đăng ký hiện tại, thực thi chúng vv) và bên ngoài lớp bạn tham chiếu đến sự kiện (do đó chỉ có thêm/xóa khả năng).

Sự thay thế cho các sự kiện giống như trường là nơi bạn triển khai chính xác việc thêm/xóa chính mình, ví dụ:

private EventHandler clickHandler; // Normal private field 

public event EventHandler Click 
{ 
    add 
    { 
     Console.WriteLine("New subscriber"); 
     clickHandler += value; 
    } 
    remove 
    { 
     Console.WriteLine("Lost a subscriber"); 
     clickHandler -= value; 
    } 
} 

Xem my article on events để biết thêm thông tin.

Dĩ nhiên nhà xuất bản sự kiện thể cũng làm cho biết thêm thông tin có sẵn - bạn có thể viết một tài sản như ClickHandlers để trả lại hiện đại đa băng bột hoặc HasClickHandlers trở lại xem có bất kỳ hay không. Đó không phải là một phần của mô hình sự kiện cốt lõi.

+0

+1 cho câu trả lời. Chỉ cần thêm vào nó, bạn có thể, tất nhiên, thực hiện các hoạt động thêm/gỡ bỏ như các phương thức tường minh với một đại biểu đa diễn viên nội bộ. Trong trường hợp đó, bạn có thể theo dõi người đăng ký và người đăng ký tự hủy - và do đó cung cấp quyền truy cập vào thông tin bổ sung (ví dụ: số người đăng ký, v.v.) - nếu bạn chọn. – LBushkin

+0

Yup - sẽ thêm điều đó, cảm ơn. –

+0

Có thể hữu ích khi lưu ý rằng các sự kiện * reason * được thực hiện theo cách này để bất kỳ lớp nào không thể thay đổi hành vi của lớp bằng sự kiện đơn giản bằng cách truy cập sự kiện (như một thuộc tính ủy nhiệm) và xóa nó danh sách người đăng ký. – womp

0

Bạn sẽ phải làm điều đó từ lớp cơ sở. Đó là lý do chính xác khiến bạn thực hiện việc này:

protected void OnSomeEvent(EventArgs e) { 
    // CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS. 
    if (SomeEvent != null) SomeEvent(this, e); 
} 

Bạn không thể truy cập sự kiện từ lớp dẫn xuất. Ngoài ra, bạn nên làm cho phương thức đó ảo, để nó có thể được ghi đè trong một lớp dẫn xuất.

+2

Xin lưu ý rằng đó không phải là chủ đề an toàn. Nó có thể ném một NullReferenceException nếu trình xử lý cuối cùng được gỡ bỏ sau khi thử nghiệm nhưng trước khi gọi. –

+0

@Jon - tôi đã đọc bài viết của bạn về điều này, nhưng tôi không nghĩ rằng điều này sẽ xảy ra trong ứng dụng web này. – gabe

2

Dưới đây là một câu hỏi hơi khác nhau

giá trị có ý nghĩa gì thử nghiệm một sự kiện bên ngoài định nghĩa cho null?

Là một người tiêu dùng bên ngoài của một sự kiện bạn chỉ có thể làm 2 hoạt động

  • Thêm một handler
  • Di chuyển một handler

Các phi nullness null hoặc của sự kiện có không mang về 2 hành động này. Tại sao bạn muốn chạy thử nghiệm không cung cấp giá trị có thể nhận biết?

+0

Tôi đang cố gắng viết một lớp công việc bao quanh cho 2 điều khiển web bên thứ ba được thiết kế kém mà tôi bắt buộc phải sử dụng cho một dự án (tôi ghét điều khiển điên khùng này). Tôi đang cố gắng để thực thi việc sử dụng một phương pháp cụ thể như xử lý sự kiện cho một sự kiện trong một đối tượng thành viên (tham chiếu đến một trong các điều khiển web) của một lớp (wrapper). tôi muốn làm một kiểm tra trong lớp wrapper để đảm bảo rằng người dùng của lớp wrapper đã thêm một trình xử lý sự kiện cụ thể vào một sự kiện của đối tượng thành viên. nó rất cụ thể với 2 điều khiển ngu ngốc này và khó giải thích ... – gabe

1

Đó là quy tắc được áp dụng khi sử dụng từ khóa 'sự kiện'.Khi bạn tạo một sự kiện, bạn đang hạn chế sự tương tác của lớp bên ngoài với ủy nhiệm cho mối quan hệ "đăng ký/hủy đăng ký", điều này bao gồm các trường hợp thừa kế. Ghi một sự kiện về cơ bản là một tài sản, nhưng đối với các cuộc gọi phương pháp, nó không phải là thực sự là một đối tượng riêng của mình, vì vậy thực sự nó trông như thế này:

public event SomeEventHandler SomeEvent 
{ 
    add 
    { 
      //Add method call to delegate 
    } 
    remove 
    { 
      //Remove method call to delegate 
    } 
} 
14

Bạn có thể dễ dàng sử dụng một cách tiếp cận rất đơn giản vào đây để đăng ký không liên tục cho một sự kiện.

Một trong 2 phương pháp dưới đây có thể được sử dụng:

  1. Cờ cách tiếp cận: _getWarehouseForVendorCompletedSubscribed là một biến tin khởi tạo thành false.

    if (!_getWarehouseForVendorCompletedSubscribed) 
        { 
         _serviceClient.GetWarehouseForVendorCompleted += new EventHandler<GetWarehouseForVendorCompletedEventArgs>(_serviceClient_GetWarehouseForVendorCompleted); 
         _getWarehouseForVendorCompletedSubscribed = true; 
        } 
    
  2. Hủy đăng ký tiếp cận: Bao gồm mọi unsubscribe bạn muốn đăng ký.

    _serviceClient.GetWarehouseForVendorCompleted -= new 
        EventHandler<GetWarehouseForVendorCompletedEventArgs> 
    (_serviceClient_GetWarehouseForVendorCompleted); 
    
    
    _serviceClient.GetWarehouseForVendorCompleted += new 
         EventHandler<GetWarehouseForVendorCompletedEventArgs> 
         (_serviceClient_GetWarehouseForVendorCompleted); 
    
+0

+1 Đây là BRILLIANT và đây chính xác là những gì tôi đang tìm kiếm.Tôi không hiểu tại sao điều này không nhận được nhiều phiếu bầu hơn –

+0

Đối với cách tiếp cận hủy đăng ký, làm thế nào bạn có thể 'xóa' một EventHandler mới nếu nó chưa được thêm? – noahnu

+3

Nếu nó chưa được thêm vào, nó sẽ đơn giản bỏ qua - = –

3

Dưới đây là câu trả lời:

using System; 
delegate void MyEventHandler(); 
class MyEvent 
{ 
    string s; 
    public event MyEventHandler SomeEvent; 
    // This is called to raise the event. 
    public void OnSomeEvent() 
    { 
     if (SomeEvent != null) 
     { 
      SomeEvent(); 
     } 

    } 

    public string IsNull 
    { 
     get 
     { 
      if (SomeEvent != null) 
       return s = "The EventHandlerList is not NULL"; 
      else return s = "The EventHandlerList is NULL"; ; 
     } 
    } 
} 

class EventDemo 
{ 
    // An event handler. 
    static void Handler() 
    { 
     Console.WriteLine("Event occurred"); 
    } 

    static void Main() 
    { 
     MyEvent evt = new MyEvent(); 
     // Add Handler() to the event list. 
     evt.SomeEvent += Handler; 
     // Raise the event. 
     //evt.OnSomeEvent(); 
     evt.SomeEvent -= Handler; 
     Console.WriteLine(evt.IsNull); 
     Console.ReadKey(); 
    } 
} 
0

Nhà xuất bản của sự kiện ngầm quá tải chỉ +=-= hoạt động, và các hoạt động khác không được thực hiện trong nhà xuất bản vì những lý do rõ ràng như đã giải thích ở trên , chẳng hạn như không muốn kiểm soát người đăng ký để thay đổi sự kiện.

Nếu chúng tôi muốn xác thực nếu một sự kiện cụ thể được đăng ký trong lớp người đăng ký, nhà xuất bản tốt hơn sẽ đặt cờ trong lớp khi sự kiện là người đăng ký và xóa cờ khi nó là người đăng ký.

Nếu người đăng ký có thể truy cập vào cờ của nhà xuất bản, rất dễ dàng xác định xem sự kiện cụ thể có phải là người đăng ký hay không bằng cách kiểm tra giá trị cờ.