2008-10-17 66 views
38

Tôi đang sử dụng C#, .NET 3.5. Tôi hiểu làm thế nào để sử dụng các sự kiện, làm thế nào để khai báo trong lớp của tôi, làm thế nào để treo chúng từ một nơi khác, vv Một ví dụ contrived:C# Events hoạt động như thế nào?

public class MyList 
{ 
    private List<string> m_Strings = new List<string>(); 
    public EventHandler<EventArgs> ElementAddedEvent; 

    public void Add(string value) 
    { 
     m_Strings.Add(value); 
     if (ElementAddedEvent != null) 
      ElementAddedEvent(value, EventArgs.Empty); 
    } 
} 

[TestClass] 
public class TestMyList 
{ 
    private bool m_Fired = false; 

    [TestMethod] 
    public void TestEvents() 
    { 
     MyList tmp = new MyList(); 
     tmp.ElementAddedEvent += new EventHandler<EventArgs>(Fired); 
     tmp.Add("test"); 
     Assert.IsTrue(m_Fired); 
    } 

    private void Fired(object sender, EventArgs args) 
    { 
     m_Fired = true; 
    } 
} 

Tuy nhiên, những gì tôi làm không hiểu, là khi một tuyên bố trình xử lý sự kiện

public EventHandler<EventArgs> ElementAddedEvent; 

Nó chưa bao giờ được khởi tạo - vì vậy, chính xác, ElementAddedEvent là gì? Nó trỏ đến điều gì? Sau đây sẽ không làm việc, bởi vì EventHandler không bao giờ được khởi tạo:

[TestClass] 
public class TestMyList 
{ 
    private bool m_Fired = false; 

    [TestMethod] 
    public void TestEvents() 
    { 
     EventHandler<EventArgs> somethingHappend; 
     somethingHappend += new EventHandler<EventArgs>(Fired); 
     somethingHappend(this, EventArgs.Empty); 
     Assert.IsTrue(m_Fired); 
    } 

    private void Fired(object sender, EventArgs args) 
    { 
     m_Fired = true; 
    } 
} 

tôi nhận thấy rằng có một EventHandler.CreateDelegate (...), nhưng tất cả các chữ ký phương pháp đề nghị này chỉ được sử dụng để gắn Các đại biểu đến một EventHandler đã tồn tại thông qua ElementAddedEvent + = new EventHandler (MyMethod).

Tôi không chắc chắn nếu những gì Tôi đang cố gắng làm sẽ giúp ... nhưng cuối cùng tôi muốn đến với một phụ huynh trừu tượng DataContext trong LINQ có trẻ em có thể đăng ký loại bảng mà họ muốn "quan sát "vì vậy tôi có thể có các sự kiện như BeforeUpdate và AfterUpdate, nhưng cụ thể cho các loại. Một cái gì đó như thế này:

public class BaseDataContext : DataContext 
{ 
    private static Dictionary<Type, Dictionary<ChangeAction, EventHandler>> m_ObservedTypes = new Dictionary<Type, Dictionary<ChangeAction, EventHandler>>(); 

    public static void Observe(Type type) 
    { 
     if (m_ObservedTypes.ContainsKey(type) == false) 
     { 
      m_ObservedTypes.Add(type, new Dictionary<ChangeAction, EventHandler>()); 

      EventHandler eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler; 
      m_ObservedTypes[type].Add(ChangeAction.Insert, eventHandler); 

      eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler; 
      m_ObservedTypes[type].Add(ChangeAction.Update, eventHandler); 

      eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler; 
      m_ObservedTypes[type].Add(ChangeAction.Delete, eventHandler); 
     } 
    } 

    public static Dictionary<Type, Dictionary<ChangeAction, EventHandler>> Events 
    { 
     get { return m_ObservedTypes; } 
    } 
} 


public class MyClass 
{ 
    public MyClass() 
    { 
     BaseDataContext.Events[typeof(User)][ChangeAction.Update] += new EventHandler(OnUserUpdate); 
    } 

    public void OnUserUpdated(object sender, EventArgs args) 
    { 
     // do something 
    } 
} 

Suy nghĩ về điều này khiến tôi nhận ra tôi không thực sự hiểu những gì đang xảy ra dưới Hod với các sự kiện - và tôi muốn hiểu :)

+0

Cũng thấy câu trả lời của tôi http://stackoverflow.com/questions/2598170/question-regarding-to-value-reference-type-of-events/3854421#3854421 –

Trả lời

64

Tôi đã viết điều này trong một số tiền hợp lý của chi tiết trong an article, nhưng đây là bản tóm tắt, giả sử bạn đang khá hài lòng với delegates mình:

  • một sự kiện chỉ là một "add" phương pháp và một phương pháp "loại bỏ", trong cùng một cách mà một tài sản thực sự chỉ là một phương thức "get" và một "set" meth od. (Trong thực tế, CLI cũng cho phép một phương thức "nâng cao/lửa", nhưng C# không bao giờ tạo ra phương thức này.) Siêu dữ liệu mô tả sự kiện với các tham chiếu đến các phương thức.
  • Khi bạn khai báo một field-like event (như ElementAddedEvent của bạn) trình biên dịch tạo ra các phương pháp và lĩnh vực tư nhân (cùng loại như các đại biểu). Trong lớp học, khi bạn tham khảo ElementAddedEvent bạn đang đề cập đến trường. Bên ngoài lớp học, bạn đang đề cập đến lĩnh vực này.
  • Khi bất kỳ ai đăng ký một sự kiện (với toán tử + =) gọi phương thức thêm. Khi họ hủy đăng ký (với toán tử - =) gọi là xóa.
  • Đối với các sự kiện giống như trường, có một số đồng bộ hóa nhưng nếu không, việc thêm/xóa chỉ cần gọi Ủy quyền. Combine/Remove để thay đổi giá trị của trường được tạo tự động. Cả hai thao tác này đều gán cho trường sao lưu - hãy nhớ rằng các đại biểu là không thay đổi. Nói cách khác, các mã được tạo tự động là rất nhiều như thế này:

    // Backing field 
    // The underscores just make it simpler to see what's going on here. 
    // In the rest of your source code for this class, if you refer to 
    // ElementAddedEvent, you're really referring to this field. 
    private EventHandler<EventArgs> __ElementAddedEvent; 
    
    // Actual event 
    public EventHandler<EventArgs> ElementAddedEvent 
    { 
        add 
        { 
         lock(this) 
         { 
          // Equivalent to __ElementAddedEvent += value; 
          __ElementAddedEvent = Delegate.Combine(__ElementAddedEvent, value); 
         } 
        } 
        remove 
        { 
         lock(this) 
         { 
          // Equivalent to __ElementAddedEvent -= value; 
          __ElementAddedEvent = Delegate.Remove(__ElementAddedEvent, value); 
         } 
        } 
    } 
    
  • Giá trị ban đầu của lĩnh vực tạo ra trong trường hợp của bạn là null - và nó sẽ luôn luôn trở thành null một lần nữa nếu tất cả các thuê bao được loại bỏ, vì đó là hành vi của Delegate.Remove.

  • Nếu bạn muốn có một "no-op" xử lý để đăng ký sự kiện của bạn, vì vậy để tránh việc kiểm tra vô hiệu, bạn có thể làm:

    public EventHandler<EventArgs> ElementAddedEvent = delegate {}; 
    

    Các delegate {} chỉ là một phương pháp vô danh mà doesn' t quan tâm đến các thông số của nó và không làm gì cả.

Nếu có bất kỳ điều gì vẫn chưa rõ ràng, vui lòng hỏi và tôi sẽ cố gắng trợ giúp!

+0

Giải thích ngắn gọn, súc tích! – DOK

+2

@ dok1: Tôi đã giải thích các sự kiện một vài lần ngay bây giờ :) Đây là một trong những lĩnh vực được hiểu rõ nhất về C#, IMO. –

+0

Để đảm bảo tôi hiểu chính xác - một sự kiện giống như trường thực sự được một đại biểu hậu thuẫn. Nếu không ai có + = trên nó, nó là null (đó là lý do tại sao chúng ta phải kiểm tra null). Đầu tiên + = trên nó thực hiện một nhiệm vụ, và thứ hai + = không một kết hợp? – Matt

-3

Dưới mui xe, sự kiện chỉ là đại biểu có quy ước gọi điện đặc biệt. (. Ví dụ, bạn không cần phải kiểm tra vô hiệu trước khi tăng một sự kiện)

Trong giả, Event.Invoke() bị phá vỡ như thế này:

Nếu tổ chức sự kiện Có Listeners Gọi mỗi người nghe đồng bộ trên chủ đề này theo thứ tự tùy ý.

Vì các sự kiện là phát đa hướng, chúng sẽ có 0 hoặc nhiều người nghe, được tổ chức trong bộ sưu tập. CLR sẽ lặp qua chúng, gọi mỗi thứ tự theo thứ tự tùy ý.

Một lưu ý quan trọng cần nhớ là trình xử lý sự kiện thực thi trong cùng một luồng khi sự kiện được nâng lên. Đó là một lỗi phổ biến về tinh thần khi nghĩ đến chúng như là tạo ra một chuỗi mới. Họ không.

+8

Bạn * làm * phải kiểm tra tính vô hiệu trước khi tăng sự kiện, trừ khi bạn cẩn thận. Sự kiện thực sự không phải là đại biểu nhiều hơn bất động sản là các lĩnh vực. Sự kiện chỉ đóng gói hành vi của "đăng ký/hủy đăng ký". –

+3

Tôi nghi ngờ rằng lệnh gọi là tùy ý. Tôi tin rằng đó là năm mươi – Odys

Các vấn đề liên quan