Sự khác biệt giữa các sự kiện và đại biểu và các ứng dụng tương ứng của nó [đã đóng]


107

Tôi không thấy lợi thế của việc sử dụng các sự kiện so với các đại biểu, ngoài việc trở thành đường cú pháp. Có lẽ tôi đang hiểu sai, nhưng có vẻ như sự kiện đó chỉ là một trình giữ chỗ cho đại biểu.

Bạn có thể giải thích cho tôi sự khác biệt và khi nào thì sử dụng? Ưu nhược điểm là gì? Mã của chúng tôi bị bắt rễ rất nhiều với các sự kiện và tôi muốn tìm hiểu sâu hơn về nó.

Khi nào bạn sẽ sử dụng đại biểu trên các sự kiện và ngược lại? Vui lòng nêu kinh nghiệm thực tế của bạn với cả hai, nói trong mã sản xuất.


Vâng gói đầu của tôi xung quanh sự khác biệt là thực sự khó khăn, họ đều giống nhau và dường như làm như vậy ở cái nhìn đầu tiên
Robert Gould

1
Xem thêm câu hỏi này .
Dimitri C.

1
Sự khác biệt giữa hai sự kiện và đại biểu là vấn đề thực tế, không phải quan điểm. Câu hỏi yêu cầu các ứng dụng tương ứng vì chúng minh họa sự khác biệt trong các vấn đề mà công nghệ giải quyết. Đây cũng không phải là vấn đề quan điểm vì không ai hỏi cái nào là tốt nhất. Không có phần nào của câu hỏi này là vấn đề quan điểm, và tuyên bố này cũng không phải là quan điểm. Theo ý kiến ​​của tôi. Bạn đã nhận được huy hiệu của mình chưa?
Peter Wone

Câu trả lời:


49

Từ quan điểm kỹ thuật, các câu trả lời khác đã giải quyết sự khác biệt.

Dưới góc độ ngữ nghĩa, sự kiện là những hành động do một đối tượng nêu ra khi đáp ứng những điều kiện nhất định. Ví dụ: lớp Cổ phiếu của tôi có một thuộc tính gọi là Giới hạn, và nó gây ra một sự kiện khi giá cổ phiếu đạt đến Giới hạn. Thông báo này được thực hiện thông qua một sự kiện. Liệu có ai thực sự quan tâm đến sự kiện này và đăng ký tham gia hay không nằm ngoài mối quan tâm của tầng lớp chủ sở hữu.

Đại biểu là một thuật ngữ chung chung hơn để mô tả một cấu trúc tương tự như một con trỏ trong thuật ngữ C / C ++. Tất cả các đại biểu trong .Net là đại biểu đa hướng. Từ góc độ ngữ nghĩa, chúng thường được sử dụng như một loại đầu vào. Đặc biệt, chúng là một cách hoàn hảo để triển khai Mô hình Chiến lược . Ví dụ: nếu tôi muốn sắp xếp một Danh sách các đối tượng, tôi có thể cung cấp chiến lược Bộ so sánh cho phương thức để cho người triển khai biết cách so sánh hai đối tượng.

Tôi đã sử dụng hai phương pháp trong mã sản xuất. Hàng tấn đối tượng dữ liệu của tôi thông báo khi các thuộc tính nhất định được đáp ứng. Ví dụ cơ bản nhất, bất cứ khi nào một thuộc tính thay đổi, một sự kiện PropertyChanged sẽ được đưa ra (xem giao diện INotifyPropertyChanged). Tôi đã sử dụng các đại biểu trong mã để cung cấp các chiến lược khác nhau để chuyển các đối tượng nhất định thành chuỗi. Ví dụ cụ thể này là một danh sách triển khai ToString () được tôn vinh cho một loại đối tượng cụ thể để hiển thị nó cho người dùng.


4
Có thể tôi đang thiếu thứ gì đó, nhưng Event Handler không phải là một loại đại biểu sao?
Powerlord

1
Câu trả lời của tôi giải quyết các câu hỏi Chỉnh sửa # 1 và # 2; sự khác biệt từ quan điểm sử dụng. Đối với mục đích của cuộc thảo luận này, chúng khác nhau, mặc dù, từ quan điểm kỹ thuật, bạn đã đúng. Hãy xem các câu trả lời khác để biết sự khác biệt về kỹ thuật.
Szymon Rozga

3
"Tất cả các đại biểu trong .Net là đại biểu đa hướng"? Ngay cả các đại biểu mà trả về giá trị?
Qwertie

5
Đúng. Để biết lịch sử, hãy xem msdn.microsoft.com/en-us/magazine/cc301816.aspx . Kiểm tra: msdn.microsoft.com/en-us/library/system.delegate.aspx . Nếu chúng trả về giá trị, giá trị được trả về là đánh giá của đại biểu cuối cùng trong chuỗi.
Szymon Rozga

đại biểu là các kiểu tham chiếu trỏ đến trình xử lý sự kiện được xác định trong lớp thuê bao. Nói cách khác, đại diện được sử dụng làm liên kết giữa sự kiện (trong nhà xuất bản) và trình xử lý sự kiện được xác định trong người đăng ký. Trong một ứng dụng, sẽ có nhiều người đăng ký cần lắng nghe một sự kiện và trong những tình huống như vậy, các đại biểu cung cấp cho chúng tôi một cách hiệu quả để liên kết nhà xuất bản và người đăng ký.
josepainumkal

55

Từ khóa eventlà một công cụ sửa đổi phạm vi cho các đại biểu đa hướng. Sự khác biệt thực tế giữa điều này và chỉ khai báo một đại biểu đa hướng như sau:

  • Bạn có thể sử dụng eventtrong một giao diện.
  • Quyền truy cập lời mời đến đại biểu đa hướng bị giới hạn ở lớp khai báo. Hành vi như thể người được ủy quyền là riêng tư để gọi. Đối với mục đích gán, quyền truy cập được chỉ định bởi một công cụ sửa đổi truy cập rõ ràng (ví dụ public event:).

Như một vấn đề quan tâm, bạn có thể áp dụng +-cho các đại biểu đa hướng, và đây là cơ sở của cú pháp +=-=để gán kết hợp các đại biểu cho các sự kiện. Ba đoạn mã này tương đương nhau:

B = new EventHandler(this.MethodB);
C = new EventHandler(this.MethodC);
A = B + C;

Mẫu hai, minh họa cả bài tập trực tiếp và bài tập kết hợp.

B = new EventHandler(this.MethodB);
C = new EventHandler(this.MethodC);
A = B;
A += C;

Mẫu ba: cú pháp quen thuộc hơn. Có thể bạn đã quen với việc gán null để loại bỏ tất cả các trình xử lý.

B = new EventHandler(this.MethodB);
C = new EventHandler(this.MethodC);
A = null;
A += B;
A += C;

Giống như thuộc tính, các sự kiện có một cú pháp đầy đủ mà không ai sử dụng. Điều này:

class myExample 
{
  internal EventHandler eh;

  public event EventHandler OnSubmit 
  { 
    add 
    {
      eh = Delegate.Combine(eh, value) as EventHandler;
    }
    remove
    {
      eh = Delegate.Remove(eh, value) as EventHandler;
    }
  }

  ...
}

... làm chính xác như sau:

class myExample 
{
  public event EventHandler OnSubmit;
}

Các phương thức thêm và loại bỏ dễ thấy hơn trong cú pháp khá phức tạp mà VB.NET sử dụng (không có quá tải toán tử).


6
+ đối với "Quyền truy cập của lời mời tới đại biểu đa hướng bị giới hạn ở lớp khai báo" - đối với tôi đó là điểm khác biệt chính giữa đại biểu và sự kiện.
RichardOD

2
Một sự khác biệt quan trọng khác (được itowlson đề cập bên dưới) là người ta không thể hủy đăng ký tất cả các trình xử lý sự kiện bằng cách gán cho một sự kiện, nhưng họ có thể làm điều đó với một đại biểu. (Nhân tiện, câu trả lời của bạn là câu trả lời hữu ích nhất đối với tôi trong số những câu trả lời này).
Roman Starkov

4
Tiện dụng như Google và stackoverflow có thể có, tất cả những điều này và hơn thế nữa đều có sẵn chi tiết đáng kinh ngạc trong thông số ngôn ngữ C #, được Microsoft cung cấp công khai miễn phí. Tôi biết rằng trên mặt của nó, chúa đã tạo ra sổ tay hướng dẫn và Jon Skeet đã nuốt nó, nhưng vẫn có những bản sao khác :)
Peter Wone

12

Sự kiện là đường cú pháp. Chúng rất ngon. Khi tôi nhìn thấy một sự kiện, tôi biết phải làm gì. Khi tôi nhìn thấy một đại biểu, tôi không chắc lắm.

Kết hợp các sự kiện với các giao diện (nhiều đường hơn) tạo thành một món ăn nhẹ ngon miệng. Các đại biểu và các lớp trừu tượng ảo thuần túy ít thú vị hơn nhiều.


đó là cách tôi cũng thấy nó. Tôi muốn lời giải thích sâu sắc và ngọt ngào hơn :)

13
Quá nhiều đường làm cho một chất béo, tuy nhiên ... = P
Erik Forbes

5

Các sự kiện được đánh dấu như vậy trong siêu dữ liệu. Điều này cho phép các nhà thiết kế như Windows Forms hoặc ASP.NET phân biệt các sự kiện với các thuộc tính đơn thuần của kiểu đại biểu và cung cấp hỗ trợ thích hợp cho chúng (hiển thị cụ thể trên tab Sự kiện của cửa sổ Thuộc tính).

Một sự khác biệt khác so với thuộc tính kiểu ủy quyền là người dùng chỉ có thể thêm và xóa trình xử lý sự kiện, trong khi với thuộc tính kiểu ủy quyền, họ có thể đặt giá trị:

someObj.SomeCallback = MyCallback;  // okay, replaces any existing callback
someObj.SomeEvent = MyHandler;  // not okay, must use += instead

Điều này giúp cô lập những người đăng ký sự kiện: Tôi có thể thêm trình xử lý của mình vào một sự kiện và bạn có thể thêm trình xử lý của mình vào cùng một sự kiện và bạn sẽ không vô tình ghi đè trình xử lý của tôi.


4

Mặc dù các sự kiện thường được thực hiện với các đại biểu đa hướng, không có yêu cầu nào mà chúng phải được sử dụng theo kiểu như vậy. Nếu một lớp hiển thị sự kiện, điều đó có nghĩa là lớp đó hiển thị hai phương thức. Về bản chất, ý nghĩa của chúng là:

  1. Đây là một đại biểu. Hãy gọi nó khi điều gì đó thú vị xảy ra.
  2. Đây là một đại biểu. Bạn nên hủy tất cả các tham chiếu đến nó ngay khi thuận tiện (và không gọi nó nữa).

Cách phổ biến nhất để một lớp xử lý một sự kiện mà nó hiển thị là xác định một đại biểu đa hướng và thêm / xóa bất kỳ đại biểu nào được chuyển đến các phương thức trên nhưng không có yêu cầu chúng hoạt động theo cách đó. Thật không may, kiến ​​trúc sự kiện không thực hiện được một số điều mà lẽ ra sẽ làm cho các phương pháp tiếp cận thay thế trở nên sạch sẽ hơn nhiều (ví dụ: để phương thức đăng ký trả về MethodInvoker, phương thức này sẽ được người đăng ký giữ; để hủy đăng ký một sự kiện, chỉ cần gọi phương thức trả về) vì vậy đại biểu phát đa hướng cho đến nay là cách tiếp cận phổ biến nhất.


4

để hiểu sự khác biệt, bạn có thể xem 2 ví dụ này

Thực hiện với các đại biểu (Hành động trong trường hợp này là một loại ủy quyền 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 điều gì đó như thế này

Animale 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 cái 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 dòng 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ể nâng lên RaiseEventchỉ cần gọi nó animal.RaiseEvent().

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

Lớp Động vật 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
    {
       public event EventHandler<ArgsSpecial> Run = delegate{} //empty delegate. In this way you are sure that value is always != null because no one outside of the class can change it

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

để gọi sự kiện

 Animale 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 thuộc tính công cộng mà là trường công cộng (với các sự kiện trình biên dịch bảo vệ các trường của bạn khỏi quyền truy cập không mong muốn)
  2. Không thể chỉ định trực tiếp sự kiện. Trong trường hợp này, bạn không thể thực hiện lỗi trước đó mà tôi đã chỉ ra khi ghi đè hành vi.
  3. Không ai bên 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 đưa vào khai báo giao diện, trong khi một trường không thể

ghi chú

EventHandler được khai báo là đại biểu sau:

public delegate void EventHandler (object sender, EventArgs e)

nó nhận một người gửi (kiểu Đối tượng) 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.

EventHAndlerThay vào đó, bạn cũng có thể sử dụng ví dụ này sử dụngEventHandler<ArgsSpecial>

tham khảo ở đây cho tài liệu về EventHandler


3

Chỉnh sửa # 1 Khi nào bạn sẽ sử dụng đại diện cho các sự kiện và so sánh với đối thủ? Vui lòng nêu kinh nghiệm thực tế của bạn với cả hai, nói trong mã sản xuất.

Khi tôi thiết kế các API của riêng mình, tôi xác định các đại biểu được truyền dưới dạng tham số cho các phương thức hoặc cho các hàm tạo của các lớp:

  • Để một phương thức có thể triển khai một mẫu 'phương pháp mẫu' đơn giản (ví dụ: PredicateActioncác đại biểu được chuyển đến các lớp thu thập chung .Net)
  • Hoặc để lớp có thể thực hiện một 'gọi lại' (thường là gọi lại một phương thức của lớp đã tạo ra nó).

Các đại biểu này thường không phải là tùy chọn trong thời gian chạy (tức là không được null).

Tôi có xu hướng không sử dụng các sự kiện; nhưng khi tôi sử dụng các sự kiện, tôi sử dụng chúng cho các sự kiện tùy ý báo hiệu cho không, một hoặc nhiều máy khách thể quan tâm, tức là khi có ý nghĩa rằng một lớp (ví dụ: System.Windows.Formlớp) nên tồn tại và chạy cho dù bất kỳ máy khách nào có đã thêm một trình xử lý sự kiện vào sự kiện của nó (ví dụ: sự kiện 'chuột xuống' của biểu mẫu tồn tại, nhưng tùy chọn cho dù bất kỳ ứng dụng khách bên ngoài nào quan tâm đến việc cài đặt trình xử lý sự kiện vào sự kiện đó).


2

Mặc dù tôi không có lý do kỹ thuật nào cho nó, nhưng tôi sử dụng các sự kiện trong mã kiểu giao diện người dùng, nói cách khác, ở các cấp cao hơn của mã và sử dụng các đại biểu cho logic nằm sâu hơn trong mã. Như tôi đã nói bạn có thể sử dụng một trong hai, nhưng tôi thấy mẫu sử dụng này hợp lý, nếu không có gì khác, nó giúp ghi lại các loại lệnh gọi lại và cả hệ thống phân cấp của chúng nữa.


Chỉnh sửa: Tôi nghĩ rằng sự khác biệt trong các cách sử dụng mà tôi có là, tôi thấy hoàn toàn có thể chấp nhận được nếu bỏ qua các sự kiện, chúng là hook / sơ khai, nếu bạn cần biết về sự kiện, hãy lắng nghe chúng, nếu bạn không quan tâm sự kiện chỉ cần bỏ qua nó. Đó là lý do tại sao tôi sử dụng chúng cho giao diện người dùng, kiểu sự kiện Javascript / Browser. Tuy nhiên, khi tôi có một người được ủy quyền, tôi mong đợi THỰC SỰ mong đợi ai đó sẽ xử lý nhiệm vụ của người được ủy quyền và ném một ngoại lệ nếu không được xử lý.


Bạn có giải thích thêm về điều đó khi tôi cũng sử dụng các evens trong UI không? Một ví dụ điển hình là đủ .... cảm ơn

1

Sự khác biệt giữa các sự kiện và đại biểu nhỏ hơn rất nhiều so với tôi từng nghĩ .. Tôi vừa đăng một video siêu ngắn trên YouTube về chủ đề này: https://www.youtube.com/watch?v=el-kKK-7SBU

Hi vọng điêu nay co ich!


2
Chào mừng bạn đến với Stack Overflow! Mặc dù về mặt lý thuyết, điều này có thể trả lời câu hỏi, nhưng tốt hơn hết bạn nên đưa các phần thiết yếu của câu trả lời vào đây và cung cấp liên kết để tham khảo.
GhostCat

1

Nếu chúng ta chỉ sử dụng ủy quyền thay cho Sự kiện thì người đăng ký có cơ hội sao chép (), gọi () chính ủy nhiệm đó như được hiển thị bên dưới trong hình ảnh. Điều nào là không đúng.

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

Đó là sự khác biệt chính b / w sự kiện và đại biểu. người đăng ký chỉ có một quyền tức là nghe các sự kiện

Lớp ConsoleLog đang đăng ký các sự kiện nhật ký qua EventLogHandler

public class ConsoleLog
{
    public ConsoleLog(Operation operation)
    {
        operation.EventLogHandler += print;
    }

    public void print(string str)
    {
        Console.WriteLine("write on console : " + str);
    }
}

Lớp FileLog đang đăng ký các sự kiện nhật ký qua EventLogHandler

public class FileLog
{
    public FileLog(Operation operation)
    {
        operation.EventLogHandler += print;
    }

    public void print(string str)
    {
        Console.WriteLine("write in File : " + str);
    }
}

Lớp hoạt động đang xuất bản các sự kiện nhật ký

public delegate void logDelegate(string str);
public class Operation
{
    public event logDelegate EventLogHandler;
    public Operation()
    {
        new FileLog(this);
        new ConsoleLog(this);
    }

    public void DoWork()
    {
        EventLogHandler.Invoke("somthing is working");
    }
}
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.