2009-06-01 27 views
5

Tôi có những gì tôi nghĩ là một "vấn đề" đơn giản mà tôi đã tìm thấy một vài giải pháp nhưng tôi không chắc chắn cách nào để đi và thực hành tốt nhất trong C#.C# :: Khi nào nên sử dụng các sự kiện hoặc tập hợp các đối tượng bắt nguồn từ một giao diện xử lý sự kiện?

Tôi có một đối tượng chính (nói một singleton) được instanciated một lần trong suốt tuổi thọ của ứng dụng. "MasterClass" này tạo ra một loạt các loại đối tượng mới, nói "SlaveClass" mỗi khi MasterClass.Instance.CreateSlaveObject được gọi.

MasterClass này cũng giám sát một số đối tượng khác để thay đổi trạng thái và khi điều đó xảy ra, thông báo cho đối tượng SlaveClass mà nó tạo ra thay đổi. Có vẻ đơn giản là đủ.

Kể từ khi tôi xuất phát từ nguồn gốc C++ trên thế giới, cách tôi đã làm nó đầu tiên nó có một giao diện

Interface IChangeEventListener 
{ 
    void ChangeHappened(); 
} 

từ mà tôi có nguồn gốc "SlaveClass". Sau đó, trong "MasterClass" của tôi, tôi có:

... 
IList<IChangeEventListener> slaveList; 
... 
CreateSlaveObject 
{ 
    ... 
    slaveList.Add(slave); 
} 
... 
ChangeHappened() 
{ 
    ... 
    foreach(var slave in slaveList) 
    { 
     slave.ChangeHappened(); 
    } 
} 

Và tính năng này hoạt động. Nhưng tôi cứ tự hỏi trong tâm trí của tôi nếu có một cách khác (tốt hơn) để làm điều này. Vì vậy, tôi đã nghiên cứu thêm một chút về chủ đề và thấy các sự kiện C#. Vì vậy, thay vì duy trì một bộ sưu tập của nô lệ trong MasterClass, tôi về cơ bản sẽ tiêm MasterClass vào ctor của SlaveClass (hoặc thông qua một tài sản) và để cho đối tượng SlaveClass thêm nó ChangeHappened như một xử lý sự kiện. điều này sẽ được minh họa:

...Master...   
    public delegate void ChangeHappenedDelegate(object sender, NewsInfoArgs args); 
    public event NewUpdateDelegate ChangeHappenedEvent; 
    .... 

    public SlaveClass (MasterClass publisher) //inject publisher service 
    { 
     publisher.ChangeHappenedEvent += ChangeHappened; 
    } 

Nhưng điều này có vẻ là giống như một khớp nối un-cần thiết giữa các Slave và Master, nhưng tôi thích sự sang trọng của các cơ chế thông báo sự kiện xây dựng-in cung cấp.

Vì vậy, tôi có nên giữ mã hiện tại của mình hoặc chuyển sang phương pháp tiếp cận dựa trên sự kiện (với nhà xuất bản tiêm) không? và tại sao?

Hoặc nếu bạn có thể đề xuất giải pháp thay thế tôi có thể đã bỏ lỡ, tôi cũng sẽ đánh giá cao điều đó.

Trả lời

7

Vâng, trong tâm trí của tôi, các sự kiện và giao diện như bạn đã cho thấy là hai mặt của cùng một đồng tiền (ít nhất là trong bối cảnh bạn mô tả nó), nhưng chúng thực sự là hai bên bên của điều này.

Cách tôi nghĩ về các sự kiện là "Tôi cần đăng ký sự kiện của bạn vì tôi cần bạn cho tôi biết khi có điều gì xảy ra với bạn".

Trong khi cách giao diện là "Tôi cần gọi phương thức để bạn thông báo với bạn rằng đã xảy ra sự cố với tôi".

Nó nghe có vẻ giống nhau, nhưng nó khác với ai đang nói, trong cả hai trường hợp, đó là "masterclass" của bạn đang nói, và điều đó tạo ra tất cả sự khác biệt.Lưu ý rằng nếu các lớp nô lệ của bạn có sẵn phương thức phù hợp để gọi khi có điều gì đó xảy ra trong lớp chủ của bạn, bạn không cần lớp nô lệ để chứa mã để nối nó lên, bạn có thể dễ dàng làm điều này trong phương pháp CreateSlaveClass của bạn:

SlaveClass sc = new SlaveClass(); 
ChangeHappenedEvent += sc.ChangeHappened; 
return sc; 

này về cơ bản sẽ sử dụng hệ thống sự kiện, nhưng hãy để mã Masterclass làm tất cả hệ thống dây điện trong những sự kiện.

Đối tượng SlaveClass có tồn tại miễn là lớp singleton không? Nếu không, thì bạn cần xử lý trường hợp khi chúng trở nên cũ/không còn cần thiết, như trong trường hợp trên (về cơ bản trong cả bạn và của tôi), bạn đang giữ một tham chiếu đến các đối tượng đó trong MasterClass của bạn, và do đó chúng sẽ không bao giờ đủ điều kiện để thu thập rác, trừ khi bạn buộc phải loại bỏ các sự kiện đó hoặc hủy đăng ký các giao diện.


Để xử lý sự cố với SlaveClass không sống miễn là MasterClass, bạn sẽ gặp phải vấn đề ghép nối giống như bạn đã lưu ý trong nhận xét.

Một cách để "xử lý" (lưu ý dấu ngoặc kép) điều này có thể không liên kết trực tiếp đến đúng phương pháp trên đối tượng SlaveClass, mà thay vào đó tạo đối tượng bao bọc nội bộ sẽ gọi phương thức này. Lợi ích từ điều này sẽ là đối tượng bao bọc có thể sử dụng đối tượng WeakReference bên trong, vì vậy khi đối tượng SlaveClass của bạn đủ điều kiện để thu thập rác, nó có thể được thu thập và sau đó bạn thử gọi đúng phương pháp trên đó sẽ nhận thấy điều này, và do đó bạn sẽ phải dọn dẹp.

Ví dụ, như thế này (và ở đây tôi đang gõ mà không có lợi ích của một Visual Studio IntelliSense và một trình biên dịch, hãy ý nghĩa của mã này, và không phải là cú pháp (lỗi).)

public class WrapperClass 
{ 
    private WeakReference _Slave; 

    public WrapperClass(SlaveClass slave) 
    { 
     _Slave = new WeakReference(slave); 
    } 

    public WrapperClass.ChangeHappened() 
    { 
     Object o = _Slave.Target; 
     if (o != null) 
      ((SlaveClass)o).ChangeHappened(); 
     else 
      MasterClass.ChangeHappenedEvent -= ChangeHappened; 
    } 
} 

trong Masterclass của bạn, bạn sẽ như vậy, làm một cái gì đó như thế này:

SlaveClass sc = new SlaveClass(); 
WrapperClass wc = new WrapperClass(sc); 
ChangeHappenedEvent += wc.ChangeHappened; 
return sc; 

Khi đối tượng SlaveClass được thu thập, các cuộc gọi tiếp theo (nhưng không sớm hơn đó) từ Masterclass của bạn để xử lý sự kiện để thông báo cho họ về thay đổi, tất cả những trình bao bọc đó không còn có một đối tượng nữa bị xóa.

+0

Gọn gàng! Tôi thực sự thích cách tiếp cận bạn đề xuất của MasterClass để chăm sóc sự kiện hooking, nhưng giữ cơ chế sự kiện :) Vấn đề tôi thấy là như bạn đã hỏi, không có gì đảm bảo rằng Slave sẽ sống miễn là Master ... vì vậy nếu tôi muốn hủy bỏ sự kiện trong trình phá hủy Slave, tôi sẽ mang lại khớp nối:/ – Futurist

+0

Vâng, có một sự thay thế, hãy để tôi thay đổi câu trả lời của tôi. –

+0

Cảm ơn bạn đã trả lời độc lập. Tôi sẽ suy nghĩ về điều này vì có một số khái niệm (như suy yếu) vẫn còn xa lạ với tôi. – Futurist

0

Tôi nghĩ rằng đó chỉ là vấn đề sở thích cá nhân ... cá nhân tôi thích sử dụng các sự kiện, vì nó phù hợp hơn trong "triết lý" .NET.

Trong trường hợp của bạn, nếu Masterclass là một singleton, bạn không cần phải vượt qua nó để các nhà xây dựng của SlaveClass, vì nó có thể được lấy ra bằng cách sử dụng tài sản singleton (hoặc phương pháp):

public SlaveClass() 
{ 
    MasterClass.Instance.ChangeHappenedEvent += ChangeHappened; 
} 
+0

Tôi đã nói dối một chút .. nó không thực sự là Singleton :) Nhưng tôi vẫn không thích rằng Slave phải nhận thức được chủ nhân ngay cả khi chủ nhân sẽ là một singleton. Cảm ơn bạn đã trả lời dù sao. – Futurist

0

Khi bạn xuất hiện để có một trường hợp singleton của MasterClass, tại sao không đăng ký MasterClass.Instance.ChangeHappenedEvent? Nó vẫn là một khớp nối chặt chẽ, nhưng tương đối gọn gàng.

+0

Tôi đã nói dối một chút ... nó không thực sự là một Singleton :) Nhưng tôi vẫn không thích rằng Slave phải được nhận thức của chủ ngay cả khi chủ nhân sẽ là một singleton. Cảm ơn bạn đã trả lời dù sao. – Futurist

0

Mặc dù sự kiện sẽ là mô hình bình thường để hiển thị chức năng đăng ký/hủy đăng ký công khai, trong nhiều trường hợp, chúng không phải là mô hình tốt nhất khi chức năng đăng ký/hủy đăng ký không cần phải được hiển thị. Trong trường hợp này, những thứ duy nhất được phép đăng ký/hủy đăng ký sự kiện là những thứ mà bạn tự tạo, vì vậy không cần phương thức đăng ký/hủy đăng ký công khai. Hơn nữa, kiến ​​thức của các bậc thầy về nô lệ vượt xa kiến ​​thức của nhà xuất bản sự kiện điển hình về người đăng ký. Do đó, tôi ủng hộ việc chủ nhân xử lý rõ ràng việc kết nối/ngắt kết nối các đăng ký.

Một tính năng tôi có thể thêm vào, được thực hiện dễ dàng hơn bằng cách ghép nối giữa chủ và phụ, sẽ là phương tiện cho phép nô lệ được thu gom rác nếu tất cả các tham chiếu bên ngoài bị bỏ qua. Cách dễ nhất để làm điều này có lẽ sẽ là để thầy giữ một danh sách 'WeakReference's để nô lệ. Khi cần phải thông báo cho nô lệ rằng có điều gì đó đã xảy ra, hãy đi qua danh sách, sự bất tuân của bất kỳ WeakReference nào vẫn còn sống, và thông báo cho nô lệ.Nếu hơn một nửa các mục trong danh sách đã được thêm kể từ lần quét cuối cùng và danh sách chứa ít nhất 250 mục hoặc hơn, hãy sao chép tất cả các tham chiếu trực tiếp vào danh sách mới và thay thế danh sách cũ.

Một cách tiếp cận thay thế sẽ tránh phải dereference đối tượng WeakReference vì vậy thường sẽ có đối tượng "nô lệ" công khai là một wrapper cho một đối tượng riêng tư và có đối tượng public override Finalize để cho đối tượng riêng biết rằng không có tham chiếu bên ngoài nào tồn tại. Điều này sẽ yêu cầu thêm một mức độ mạnh mẽ hơn cho sự truy cập công khai đối tượng, nhưng nó sẽ tránh tạo ra - ngay cả trong giây lát - các tham chiếu mạnh mẽ đến các đối tượng bị bỏ rơi.

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