Sự khác biệt giữa các đại biểu và sự kiện là gì?


317

Sự khác biệt giữa các đại biểu và một sự kiện là gì? Không cả hai giữ tham chiếu đến các chức năng có thể được thực thi?



2
điều này giải thích với ví dụ có một cái nhìn unitygeek.com/delegates-events-unity
Rahul Lalit

Câu trả lời:


283

Một tuyên bố sự kiện thêm một lớp trừu tượng và bảo vệ trên cá thể đại biểu . Sự bảo vệ này ngăn khách hàng của đại biểu đặt lại đại biểu và danh sách gọi của nó và chỉ cho phép thêm hoặc xóa các mục tiêu khỏi danh sách gọi.


44
Nếu tất nhiên, lớp bảo vệ này cũng ngăn "các máy khách" (mã bên ngoài lớp / struct xác định) gọi ra ủy nhiệm và không nhận được bất kỳ cách nào đối tượng ủy nhiệm "đằng sau" sự kiện.
Jeppe Stig Nielsen

7
Không hoàn toàn đúng. Bạn có thể khai báo một sự kiện mà không có ví dụ đại biểu phụ trợ. Trong c #, bạn có thể triển khai một sự kiện một cách rõ ràng và sử dụng cấu trúc dữ liệu phụ trợ khác mà bạn chọn.
Miguel Gamboa

3
@mmcdole bạn có thể cung cấp một ví dụ để giải thích của mình?
vivek nuna

103

Để hiểu sự khác biệt bạn có thể nhìn vào 2 ví dụ này

Ví dụ với Đại biểu (trong trường hợp này là Hành động - đó là một loại đại biểu không trả về giá trị)

public class Animal
{
    public Action Run {get; set;}

    public void RaiseEvent()
    {
        if (Run != null)
        {
            Run();
        }
    }
}

Để sử dụng đại biểu, bạn nên làm một cái gì đó như thế này:

Animal animal= new Animal();
animal.Run += () => Console.WriteLine("I'm running");
animal.Run += () => Console.WriteLine("I'm still running") ;
animal.RaiseEvent();

Mã này hoạt động tốt nhưng bạn có thể có một số điểm yếu.

Ví dụ, nếu tôi viết điều này:

animal.Run += () => Console.WriteLine("I'm running");
animal.Run += () => Console.WriteLine("I'm still running");
animal.Run = () => Console.WriteLine("I'm sleeping") ;

với dòng mã cuối cùng, tôi đã ghi đè các hành vi trước đó chỉ với một hành vi bị thiếu +(tôi đã sử dụng =thay vì +=)

Một điểm yếu khác là mọi lớp sử dụng Animallớp của bạn có thể tăng RaiseEventchỉ bằng cách gọi nó animal.RaiseEvent().

Để tránh những điểm yếu này, bạn có thể sử dụng eventstrong c #.

Lớp Thú của bạn sẽ thay đổi theo cách này:

public class ArgsSpecial : EventArgs
{
    public ArgsSpecial (string val)
    {
        Operation=val;
    }

    public string Operation {get; set;}
} 

public class Animal
{
    // Empty delegate. In this way you are sure that value is always != null 
    // because no one outside of the class can change it.
    public event EventHandler<ArgsSpecial> Run = delegate{} 

    public void RaiseEvent()
    {  
         Run(this, new ArgsSpecial("Run faster"));
    }
}

để gọi các sự kiện

 Animal animal= new Animal();
 animal.Run += (sender, e) => Console.WriteLine("I'm running. My value is {0}", e.Operation);
 animal.RaiseEvent();

Sự khác biệt:

  1. Bạn không sử dụng tài sản công cộng mà là trường công cộng (sử dụng các sự kiện, trình biên dịch sẽ bảo vệ các trường của bạn khỏi sự truy cập không mong muốn)
  2. Sự kiện không thể được chỉ định trực tiếp. Trong trường hợp này, nó sẽ không làm phát sinh lỗi trước đó mà tôi đã thể hiện với việc ghi đè hành vi.
  3. Không ai ngoài lớp học của bạn có thể nâng cao sự kiện.
  4. Các sự kiện có thể được bao gồm trong một khai báo giao diện, trong khi một trường không thể

Ghi chú:

EventHandler được tuyên bố là đại biểu sau:

public delegate void EventHandler (object sender, EventArgs e)

nó cần một người gửi (thuộc loại Object) và các đối số sự kiện. Người gửi là null nếu nó đến từ các phương thức tĩnh.

Ví dụ này, sử dụng EventHandler<ArgsSpecial>, cũng có thể được viết bằng cách sử dụngEventHandler thay thế.

Tham khảo tại đây để có tài liệu về EventHandler


7
Mọi thứ đều tuyệt vời cho đến khi tôi chạy vào "Không ai ngoài lớp bạn có thể nâng cao sự kiện." Điều đó nghĩa là gì? Không ai có thể gọi RaiseEventmiễn là một phương thức gọi có quyền truy cập vào một thể hiện animaltrong mã sử dụng sự kiện?
dance2die

11
Sự kiện @Sung chỉ có thể được tăng lên từ bên trong lớp học, có lẽ tôi đã không giải thích rõ ràng về điều đó. Với các sự kiện bạn có thể gọi hàm nâng cao sự kiện (đóng gói), nhưng nó chỉ có thể được tăng lên từ bên trong lớp định nghĩa nó. Hãy cho tôi biết nếu tôi không rõ ràng.
faby

1
"Sự kiện không thể được chỉ định trực tiếp." Trừ khi tôi hiểu bạn sai, điều này không đúng. Dưới đây là một ví dụ: gist.github.com/Chiel92/36bb3a2d2ac7dd511b96
Chiel ten Brinke

2
@faby, Ý bạn là mặc dù sự kiện được tuyên bố là công khai nhưng tôi vẫn không thể làm gì animal.Run(this, new ArgsSpecial("Run faster");?
Pap

1
@ChieltenBrinke Tất nhiên sự kiện có thể được chỉ định trong các thành viên của lớp ... nhưng không thì khác.
Jim Balter

94

Ngoài các thuộc tính cú pháp và hoạt động, còn có một sự khác biệt về ngữ nghĩa.

Các đại biểu, về mặt khái niệm, các mẫu chức năng; nghĩa là, họ thể hiện một hợp đồng mà một chức năng phải tuân thủ để được coi là "loại" của đại biểu.

Sự kiện đại diện cho ... tốt, sự kiện. Họ dự định cảnh báo ai đó khi có chuyện gì xảy ra và vâng, họ tuân thủ định nghĩa đại biểu nhưng họ không giống nhau.

Ngay cả khi chúng giống hệt nhau (về mặt cú pháp và mã IL) vẫn sẽ có sự khác biệt về ngữ nghĩa. Nói chung, tôi thích có hai tên khác nhau cho hai khái niệm khác nhau, ngay cả khi chúng được thực hiện theo cùng một cách (điều đó không có nghĩa là tôi muốn có cùng một mã hai lần).


8
Mô tả tuyệt vời của các đại biểu.
Sampson

1
Vì vậy, chúng ta có thể nói rằng một sự kiện là một loại "đặc biệt" của một đại biểu?
Pap

Tôi không hiểu ý của bạn. Bạn có thể sử dụng một đại biểu để 'cảnh báo cho ai đó khi có chuyện gì xảy ra'. Có thể bạn sẽ không làm điều đó, nhưng bạn có thể và do đó nó không phải là một tài sản cố hữu của sự kiện.
steve

@Jorge Córdoba ví dụ về đại biểu và sự kiện đại biểu là chủ sở hữu tờ báo và sự kiện (Đăng ký hoặc hủy đăng ký) và một số người mua báo và một số người không mua báo có nghĩa là chủ sở hữu tờ báo không thể buộc mỗi người mua tờ báo của tôi đúng hay sai?
Rahul_Patil

37

Đây là một liên kết tốt để tham khảo. http://csharpindepth.com/Articles/Ch CHƯƠNG2 / Events.aspx

Tóm lại, lấy đi từ bài viết - Sự kiện được gói gọn trong các đại biểu.

Trích dẫn từ bài viết:

Giả sử các sự kiện không tồn tại như một khái niệm trong C # /. NET. Làm thế nào một lớp khác đăng ký một sự kiện? Ba lựa chọn:

  1. Một biến đại biểu công khai

  2. Một biến đại biểu được hỗ trợ bởi một thuộc tính

  3. Một biến đại biểu với các phương thức AddXXXHandler và RemoveXXXHandler

Tùy chọn 1 rõ ràng là khủng khiếp, vì tất cả các lý do bình thường, chúng tôi ghê tởm các biến công khai.

Tùy chọn 2 tốt hơn một chút, nhưng cho phép các thuê bao ghi đè lên nhau một cách hiệu quả - sẽ rất dễ dàng để viết someInstance.MyEvent = eventHandler; mà sẽ thay thế bất kỳ trình xử lý sự kiện hiện có thay vì thêm một trình xử lý mới. Ngoài ra, bạn vẫn cần phải viết các thuộc tính.

Tùy chọn 3 về cơ bản là những gì các sự kiện mang lại cho bạn, nhưng với một quy ước được bảo đảm (được tạo bởi trình biên dịch và được hỗ trợ bởi các cờ bổ sung trong IL) và triển khai "miễn phí" nếu bạn hài lòng với ngữ nghĩa mà các sự kiện giống như trường cung cấp cho bạn. Đăng ký và hủy đăng ký khỏi các sự kiện được gói gọn mà không cho phép truy cập tùy ý vào danh sách các trình xử lý sự kiện và ngôn ngữ có thể làm mọi thứ đơn giản hơn bằng cách cung cấp cú pháp cho cả khai báo và đăng ký.


Giải thích hay và súc tích. Thanx
Pap

Đây là mối quan tâm về mặt lý thuyết nhiều hơn bất cứ điều gì, nhưng FWIW tôi luôn cảm thấy như đối số "Tùy chọn 1 là xấu vì chúng ta không thích các biến công khai" có thể sử dụng rõ ràng hơn một chút. Nếu anh ta nói rằng vì đó là "thực hành OOP xấu", về mặt kỹ thuật , một public Delegatebiến sẽ phơi bày "dữ liệu", nhưng theo hiểu biết tốt nhất của tôi thì OOP không bao giờ đề cập đến bất kỳ khái niệm nào giống như Delegate(đó không phải là "đối tượng" hay "thông điệp") và .NET thực sự hầu như không đối xử với các đại biểu như dữ liệu.
jrh

Mặc dù tôi cũng muốn đưa ra lời khuyên thiết thực hơn, nếu bạn đang ở trong một tình huống mà bạn muốn đảm bảo rằng chỉ có một trình xử lý, thực hiện các AddXXXHandlerphương thức của riêng bạn với một private Delegatebiến có thể là một lựa chọn tốt. Trong trường hợp này, bạn có thể kiểm tra xem liệu trình xử lý đã được đặt chưa và phản ứng thích hợp. Đây cũng có thể là một thiết lập tốt nếu bạn cần đối tượng đang giữ Delegateđể có thể xóa tất cả các trình xử lý ( eventkhông cung cấp cho bạn bất kỳ cách nào để làm điều này).
jrh

7

LƯU Ý: Nếu bạn có quyền truy cập vào C # 5.0 Unleashed , hãy đọc phần "Hạn chế đối với việc sử dụng đại biểu" trong Chương 18 có tiêu đề "Sự kiện" để hiểu rõ hơn về sự khác biệt giữa hai điều này.


Nó luôn giúp tôi có một ví dụ đơn giản, cụ thể. Vì vậy, đây là một cho cộng đồng. Đầu tiên tôi chỉ ra cách bạn có thể sử dụng các đại biểu một mình để làm những gì Sự kiện làm cho chúng tôi. Sau đó, tôi chỉ ra cách giải pháp tương tự sẽ hoạt động với một thể hiện của EventHandler. Và sau đó tôi giải thích lý do tại sao chúng ta KHÔNG muốn làm những gì tôi giải thích trong ví dụ đầu tiên. Bài viết này được lấy cảm hứng từ một bài viết của John Skeet.

Ví dụ 1: Sử dụng đại biểu công cộng

Giả sử tôi có một ứng dụng WinForms với một hộp thả xuống duy nhất. Việc thả xuống bị ràng buộc với một List<Person>. Trường hợp Person có các thuộc tính của Id, Name, NickName, HairColor. Trên biểu mẫu chính là điều khiển người dùng tùy chỉnh hiển thị các thuộc tính của người đó. Khi ai đó chọn một người trong trình đơn thả xuống, nhãn trong bản cập nhật kiểm soát người dùng sẽ hiển thị các thuộc tính của người được chọn.

nhập mô tả hình ảnh ở đây

Đây là cách nó hoạt động. Chúng tôi có ba tệp giúp chúng tôi kết hợp chúng lại với nhau:

  • Mediator.cs - lớp tĩnh giữ các đại biểu
  • Form1.cs - hình thức chính
  • Chi tiếtView.cs - kiểm soát người dùng hiển thị tất cả các chi tiết

Đây là mã có liên quan cho mỗi lớp:

class Mediator
{
    public delegate void PersonChangedDelegate(Person p); //delegate type definition
    public static PersonChangedDelegate PersonChangedDel; //delegate instance. Detail view will "subscribe" to this.
    public static void OnPersonChanged(Person p) //Form1 will call this when the drop-down changes.
    {
        if (PersonChangedDel != null)
        {
            PersonChangedDel(p);
        }
    }
}

Đây là kiểm soát người dùng của chúng tôi:

public partial class DetailView : UserControl
{
    public DetailView()
    {
        InitializeComponent();
        Mediator.PersonChangedDel += DetailView_PersonChanged;
    }

    void DetailView_PersonChanged(Person p)
    {
        BindData(p);
    }

    public void BindData(Person p)
    {
        lblPersonHairColor.Text = p.HairColor;
        lblPersonId.Text = p.IdPerson.ToString();
        lblPersonName.Text = p.Name;
        lblPersonNickName.Text = p.NickName;

    }
}

Cuối cùng, chúng ta có đoạn mã sau trong Form1.cs. Ở đây chúng tôi đang gọi OnPersonChanged, gọi bất kỳ mã nào được đăng ký cho đại biểu.

private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
    Mediator.OnPersonChanged((Person)comboBox1.SelectedItem); //Call the mediator's OnPersonChanged method. This will in turn call all the methods assigned (i.e. subscribed to) to the delegate -- in this case `DetailView_PersonChanged`.
}

Đồng ý. Vì vậy, đó là cách bạn sẽ làm cho điều này hoạt động mà không cần sử dụng các sự kiệnchỉ sử dụng các đại biểu . Chúng tôi chỉ cần đưa một đại biểu công khai vào một lớp - bạn có thể làm cho nó tĩnh hoặc đơn lẻ, hoặc bất cứ điều gì. Tuyệt quá.

NHƯNG, NHƯNG, NHƯNG, chúng tôi không muốn làm những gì tôi vừa mô tả ở trên. Bởi vì các lĩnh vực công cộng là xấu cho nhiều, nhiều lý do. Vậy lựa chọn của chúng ta là gì? Như John Skeet mô tả, đây là các lựa chọn của chúng tôi:

  1. Một biến đại biểu công khai (đây là những gì chúng ta vừa làm ở trên. Đừng làm điều này. Tôi chỉ nói với bạn ở trên tại sao nó xấu)
  2. Đặt đại biểu vào một thuộc tính bằng get / set (vấn đề ở đây là các thuê bao có thể ghi đè lẫn nhau - vì vậy chúng tôi có thể đăng ký một loạt các phương thức cho đại biểu và sau đó chúng tôi có thể vô tình nói PersonChangedDel = null , xóa sạch tất cả các đăng ký khác. vấn đề khác vẫn còn ở đây là vì người dùng có quyền truy cập vào đại biểu, họ có thể gọi các mục tiêu trong danh sách gọi - chúng tôi không muốn người dùng bên ngoài có quyền truy cập khi nào sẽ tăng sự kiện của chúng tôi.
  3. Một biến đại biểu với các phương thức AddXXXHandler và RemoveXXXHandler

Tùy chọn thứ ba này về cơ bản là những gì một sự kiện mang lại cho chúng ta. Khi chúng tôi khai báo EventHandler, nó cho phép chúng tôi truy cập vào một đại biểu - không công khai, không phải là một tài sản, nhưng vì điều này chúng tôi gọi là một sự kiện vừa thêm / xóa người truy cập.

Chúng ta hãy xem cùng một chương trình trông như thế nào, nhưng bây giờ sử dụng Sự kiện thay vì đại biểu công khai (Tôi cũng đã thay đổi Người hòa giải của chúng tôi thành người độc thân):

Ví dụ 2: Với EventHandler thay vì đại biểu công khai

Người hòa giải:

class Mediator
{

    private static readonly Mediator _Instance = new Mediator();

    private Mediator() { }

    public static Mediator GetInstance()
    {
        return _Instance;
    }

    public event EventHandler<PersonChangedEventArgs> PersonChanged; //this is just a property we expose to add items to the delegate.

    public void OnPersonChanged(object sender, Person p)
    {
        var personChangedDelegate = PersonChanged as EventHandler<PersonChangedEventArgs>;
        if (personChangedDelegate != null)
        {
            personChangedDelegate(sender, new PersonChangedEventArgs() { Person = p });
        }
    }
}

Lưu ý rằng nếu bạn F12 trên EventHandler, nó sẽ hiển thị cho bạn định nghĩa chỉ là một đại biểu có ý nghĩa chung với đối tượng "người gửi" bổ sung:

public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);

Kiểm soát người dùng:

public partial class DetailView : UserControl
{
    public DetailView()
    {
        InitializeComponent();
        Mediator.GetInstance().PersonChanged += DetailView_PersonChanged;
    }

    void DetailView_PersonChanged(object sender, PersonChangedEventArgs e)
    {
        BindData(e.Person);
    }

    public void BindData(Person p)
    {
        lblPersonHairColor.Text = p.HairColor;
        lblPersonId.Text = p.IdPerson.ToString();
        lblPersonName.Text = p.Name;
        lblPersonNickName.Text = p.NickName;

    }
}

Cuối cùng, đây là mã Form1.cs:

private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
        Mediator.GetInstance().OnPersonChanged(this, (Person)comboBox1.SelectedItem);
}

Vì EventHandler muốn và EventArss làm tham số, tôi đã tạo lớp này chỉ với một thuộc tính duy nhất trong đó:

class PersonChangedEventArgs
{
    public Person Person { get; set; }
}

Hy vọng rằng điều đó cho bạn thấy một chút về lý do tại sao chúng tôi có các sự kiện và chúng khác nhau như thế nào - nhưng về mặt chức năng giống nhau - như các đại biểu.


Mặc dù tôi đánh giá cao tất cả các công việc tốt trong bài đăng này và tôi thích đọc hầu hết nó, tôi vẫn cảm thấy một vấn đề không được giải quyết - The other problem that remains here is that since the users have access to the delegate, they can invoke the targets in the invocation list -- we don't want external users having access to when to raise our events. Trong phiên bản mới nhất của Mediator, bạn vẫn có thể gọi OnPersonChangebất cứ khi nào bạn có một tham chiếu đến singleton. Có lẽ bạn nên đề cập rằng Mediatorphương pháp này không ngăn chặn hành vi cụ thể đó và gần với xe buýt sự kiện hơn.
Ivaylo Slavov

6

Bạn cũng có thể sử dụng các sự kiện trong khai báo giao diện, không phải như vậy đối với các đại biểu.


2
Giao diện @surfen có thể chứa các sự kiện, nhưng không phải là đại biểu.
Alexandr Nikitin

1
Chính xác ý của bạn là gì? Bạn có thể có Action a { get; set; }một định nghĩa giao diện.
Chiel ten Brinke

6

Thật là một sự hiểu lầm lớn giữa các sự kiện và đại biểu !!! Một đại biểu chỉ định một LOẠI (chẳng hạn như một classhoặc một interface), trong khi một sự kiện chỉ là một loại MEMBER (chẳng hạn như các trường, thuộc tính, v.v.). Và, giống như bất kỳ loại thành viên nào khác, một sự kiện cũng có một loại. Tuy nhiên, trong trường hợp của một sự kiện, loại sự kiện phải được chỉ định bởi một đại biểu. Chẳng hạn, bạn KHÔNG THỂ khai báo một sự kiện thuộc loại được xác định bởi giao diện.

Kết luận, chúng ta có thể thực hiện Quan sát sau : loại sự kiện PHẢI được xác định bởi một đại biểu . Đây là mối quan hệ chính giữa một sự kiện và đại biểu và được mô tả trong phần II.18 Xác định các sự kiện của ECMA-335 (CLI) Phân vùng I đến VI :

Trong cách sử dụng thông thường, TypeSpec (nếu có) xác định một đại biểu có chữ ký khớp với các đối số được truyền cho phương thức chữa cháy của sự kiện.

Tuy nhiên, thực tế này KHÔNG ngụ ý rằng một sự kiện sử dụng trường đại biểu ủng hộ . Trong thực tế, một sự kiện có thể sử dụng trường sao lưu của bất kỳ loại cấu trúc dữ liệu khác nhau mà bạn chọn. Nếu bạn triển khai một sự kiện một cách rõ ràng trong C #, bạn có thể tự do chọn cách bạn lưu trữ các trình xử lý sự kiện (lưu ý rằng các trình xử lý sự kiện là các thể hiện của loại sự kiện , do đó bắt buộc là một loại đại biểu --- từ Quan sát trước đó ). Nhưng, bạn có thể lưu trữ các trình xử lý sự kiện đó (là các thể hiện ủy nhiệm) trong cấu trúc dữ liệu, chẳng hạn như Listmột Dictionaryhoặc bất kỳ thứ gì khác, hoặc thậm chí trong trường đại biểu dự phòng. Nhưng đừng quên rằng bạn KHÔNG bắt buộc phải sử dụng trường đại biểu.


4

Một sự kiện trong .net là sự kết hợp được chỉ định của phương thức Thêm và phương thức Xóa, cả hai đều mong đợi một số loại đại biểu cụ thể. Cả C # và vb.net đều có thể tự động tạo mã cho các phương thức thêm và xóa sẽ xác định một đại biểu để giữ các đăng ký sự kiện và thêm / xóa thông qua ủy nhiệm cho / từ đại biểu đăng ký đó. VB.net cũng sẽ tự động tạo mã (với câu lệnh RaiseEvent) để gọi danh sách đăng ký khi và chỉ khi nó không trống; vì một số lý do, C # không tạo ra cái sau.

Lưu ý rằng mặc dù thông thường để quản lý đăng ký sự kiện bằng cách sử dụng đại biểu phát đa hướng, đó không phải là phương tiện duy nhất để làm như vậy. Từ góc độ công khai, người đăng ký sự kiện sẽ cần biết cách để cho đối tượng biết họ muốn nhận sự kiện, nhưng không cần biết nhà xuất bản sẽ sử dụng cơ chế nào để nâng cao sự kiện. Cũng lưu ý rằng trong khi bất kỳ ai xác định cấu trúc dữ liệu sự kiện trong .net dường như nghĩ rằng nên có một phương tiện công khai để nâng cao chúng, thì cả C # và vb.net đều không sử dụng tính năng đó.


3

Để xác định về sự kiện theo cách đơn giản:

Sự kiện là một TÀI LIỆU THAM KHẢO cho một đại biểu với hai hạn chế

  1. Không thể được gọi trực tiếp
  2. Không thể gán giá trị trực tiếp (ví dụ: eventObj = ủy nhiệmMethod)

Trên hai là những điểm yếu cho các đại biểu và nó được giải quyết trong sự kiện. Mẫu mã hoàn chỉnh để hiển thị sự khác biệt trong fiddler tại đây https://dotnetfiddle.net/5iR3fB .

Chuyển đổi nhận xét giữa Sự kiện và Đại biểu và mã máy khách gọi / gán giá trị cho đại biểu để hiểu sự khác biệt

Đây là mã nội tuyến.

 /*
This is working program in Visual Studio.  It is not running in fiddler because of infinite loop in code.
This code demonstrates the difference between event and delegate
        Event is an delegate reference with two restrictions for increased protection

            1. Cannot be invoked directly
            2. Cannot assign value to delegate reference directly

Toggle between Event vs Delegate in the code by commenting/un commenting the relevant lines
*/

public class RoomTemperatureController
{
    private int _roomTemperature = 25;//Default/Starting room Temperature
    private bool _isAirConditionTurnedOn = false;//Default AC is Off
    private bool _isHeatTurnedOn = false;//Default Heat is Off
    private bool _tempSimulator = false;
    public  delegate void OnRoomTemperatureChange(int roomTemperature); //OnRoomTemperatureChange is a type of Delegate (Check next line for proof)
    // public  OnRoomTemperatureChange WhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above), 
    public  event OnRoomTemperatureChange WhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above), 

    public RoomTemperatureController()
    {
        WhenRoomTemperatureChange += InternalRoomTemperatuerHandler;
    }
    private void InternalRoomTemperatuerHandler(int roomTemp)
    {
        System.Console.WriteLine("Internal Room Temperature Handler - Mandatory to handle/ Should not be removed by external consumer of ths class: Note, if it is delegate this can be removed, if event cannot be removed");
    }

    //User cannot directly asign values to delegate (e.g. roomTempControllerObj.OnRoomTemperatureChange = delegateMethod (System will throw error)
    public bool TurnRoomTeperatureSimulator
    {
        set
        {
            _tempSimulator = value;
            if (value)
            {
                SimulateRoomTemperature(); //Turn on Simulator              
            }
        }
        get { return _tempSimulator; }
    }
    public void TurnAirCondition(bool val)
    {
        _isAirConditionTurnedOn = val;
        _isHeatTurnedOn = !val;//Binary switch If Heat is ON - AC will turned off automatically (binary)
        System.Console.WriteLine("Aircondition :" + _isAirConditionTurnedOn);
        System.Console.WriteLine("Heat :" + _isHeatTurnedOn);

    }
    public void TurnHeat(bool val)
    {
        _isHeatTurnedOn = val;
        _isAirConditionTurnedOn = !val;//Binary switch If Heat is ON - AC will turned off automatically (binary)
        System.Console.WriteLine("Aircondition :" + _isAirConditionTurnedOn);
        System.Console.WriteLine("Heat :" + _isHeatTurnedOn);

    }

    public async void SimulateRoomTemperature()
    {
        while (_tempSimulator)
        {
            if (_isAirConditionTurnedOn)
                _roomTemperature--;//Decrease Room Temperature if AC is turned On
            if (_isHeatTurnedOn)
                _roomTemperature++;//Decrease Room Temperature if AC is turned On
            System.Console.WriteLine("Temperature :" + _roomTemperature);
            if (WhenRoomTemperatureChange != null)
                WhenRoomTemperatureChange(_roomTemperature);
            System.Threading.Thread.Sleep(500);//Every second Temperature changes based on AC/Heat Status
        }
    }

}

public class MySweetHome
{
    RoomTemperatureController roomController = null;
    public MySweetHome()
    {
        roomController = new RoomTemperatureController();
        roomController.WhenRoomTemperatureChange += TurnHeatOrACBasedOnTemp;
        //roomController.WhenRoomTemperatureChange = null; //Setting NULL to delegate reference is possible where as for Event it is not possible.
        //roomController.WhenRoomTemperatureChange.DynamicInvoke();//Dynamic Invoke is possible for Delgate and not possible with Event
        roomController.SimulateRoomTemperature();
        System.Threading.Thread.Sleep(5000);
        roomController.TurnAirCondition (true);
        roomController.TurnRoomTeperatureSimulator = true;

    }
    public void TurnHeatOrACBasedOnTemp(int temp)
    {
        if (temp >= 30)
            roomController.TurnAirCondition(true);
        if (temp <= 15)
            roomController.TurnHeat(true);

    }
    public static void Main(string []args)
    {
        MySweetHome home = new MySweetHome();
    }


}

2

Delegate là một con trỏ hàm an toàn kiểu. Sự kiện là việc triển khai mẫu thiết kế nhà xuất bản-thuê bao sử dụng đại biểu.


0

Nếu bạn kiểm tra Ngôn ngữ trung gian, bạn sẽ biết trình biên dịch .net chuyển đổi ủy nhiệm thành một lớp kín trong IL với một số hàm dựng sẵn, như invoke, BeginInvoke, endInvoke và lớp ủy nhiệm được kế thừa từ một lớp khác, có thể được gọi là "SystemMulticast". Tôi đoán Sự kiện là một lớp con của Đại biểu với một số thuộc tính bổ sung.

Sự khác biệt giữa thể hiện của sự kiện và đại biểu là, bạn không thể chạy sự kiện bên ngoài khai báo. Nếu bạn khai báo một sự kiện trong lớp A, bạn chỉ có thể chạy sự kiện này trong lớp A. Nếu bạn khai báo một đại biểu trong Lớp A, bạn có thể sử dụng đại biểu này ở bất cứ đâu. Tôi nghĩ rằng đây là một sự khác biệt chính giữa họ

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.