Por que precisamos da palavra-chave “evento” ao definir eventos?


108

Não entendo por que precisamos da palavra-chave "evento" ao definir eventos, quando podemos fazer a mesma coisa sem usar a palavra-chave "evento", apenas usando os delegados.

por exemplo

public delegate void CustomEventHandler(int a, string b);
public event CustomEventHandler customEvent;
customEvent += new CustomEventHandler(customEventHandler);
customEvent(1,"a"); // Raising the event

Aqui, se eu remover a palavra-chave "evento" da segunda linha, também posso gerar o evento invocando o delegado. Alguém pode me dizer por que essa palavra-chave de evento é necessária?


ok se você não usar a palavra-chave do evento, alguém que pode acessar esse evento usando o objeto de classe, defina-o como NULL como objClass.SelectedIndexChanged = null. isso irá travar seu código subjacente. a palavra-chave do evento força o usuário a atribuir algo semelhante a delegar usando + =.
Sumit Kapadia

Respostas:


141

Eventos semelhantes a campos e campos públicos de tipos de delegado parecem semelhantes, mas na verdade são muito diferentes.

Um evento é fundamentalmente como uma propriedade - é um par de métodos adicionar / remover (em vez de obter / definir uma propriedade). Quando você declara um evento semelhante a um campo (ou seja, aquele em que você não especifica os bits de adição / remoção), um evento público é criado e um campo de apoio privado. Isso permite que você crie o evento de forma privada, mas permite a inscrição pública. Com um campo de delegado público, qualquer um pode remover manipuladores de eventos de outras pessoas, gerar o evento por conta própria, etc - é um desastre de encapsulamento.

Para mais informações sobre eventos (e delegados), leia meu artigo sobre este tópico . (Em algum momento, preciso atualizar isso para C # 4, que muda levemente eventos semelhantes a campos. A essência disso ainda está correta.)


17
Isso é mil vezes melhor do que a explicação oficial de uma linha do MSDN: 'A palavra-chave do evento é usada para declarar um evento em uma classe de editor.'
cowlinator

37

A palavra-chave do evento faz três coisas diferentes:

  1. Você pode definir um evento em uma interface, embora não possa definir campos regulares em interfaces.
  2. Ele altera a visibilidade dos operadores =e ()(atribuição e invocação) para privado, de modo que apenas a classe que o contém pode chamar o evento ou substituir todos os métodos contidos nele. Os operadores -=e +=ainda podem ser chamados em um evento de fora da classe que o define (eles obtêm o modificador de acesso que você escreveu ao lado do evento).
  3. Você também pode substituir a maneira -=e o +=comportamento em eventos.

2
você> MSDN. Obrigado.
M. Azyoksul

26

As outras respostas estão bem; Eu gostaria apenas de acrescentar algo em que pensar.

Sua pergunta é "por que precisamos de eventos quando temos campos do tipo delegado?" Eu estenderia essa pergunta: por que você precisa de métodos, propriedades, eventos, construtores de instância ou finalizadores se você tem campos do tipo delegado? Por que você precisa de algo diferente de campos que contêm valores e delegados em um tipo? Por que não apenas dizer

class C
{
    private int z;
    public readonly Func<int, int> M = (int x)=>{ return x+z; }
    // ... and so on
}

?

Você não precisa de métodos, propriedades ou eventos. Fornecemos tudo isso porque os padrões de design de método, propriedade e evento são importantes e úteis, e merecem ter uma maneira padrão, documentada e clara de implementá-los na linguagem.


4
Uau! Isso me lembra porque eu amo c #! De todas as linguagens com que trabalhei, ela tem o equilíbrio certo entre compactação, flexibilidade e semântica legível. A única linguagem comparável é Object Pascal.
ATL_DEV

1
@ATL_DEV: Há uma razão para isso. O arquiteto da linguagem C #, Anders Hejlsberg, foi anteriormente o arquiteto do Delphi, que era uma linguagem baseada em Object Pascal.
Eric Lippert,

9

Em parte, é necessário porque, se você omitir a eventpalavra-chave, ele quebra o encapsulamento. Se for apenas um delegado multicast público, qualquer pessoa pode invocá-lo, defini-lo como nulo ou adulterá-lo. Se uma classe chamada MailNotifierexiste e tem um evento chamado MailReceived, não faz sentido que outros tipos possam disparar esse evento por meio de chamada mailNotifier.MailReceived();

Por outro lado, você só pode interferir e invocar eventos "semelhantes a campo" do tipo que o definiu.

Se você quiser manter sua invocação de evento privada, não há nada que o impeça de fazer algo assim:

public class MyClassWithNonFieldLikeEvent
{
   private CustomEventHandler m_delegate;

   public void Subscribe(CustomEventHandler handler) 
   {
      m_delegate += handler;        
   }

   public void Unsubscribe(CustomEventHandler handler)
   {          
      m_delegate -= handler;
   }

   private void DoSomethingThatRaisesEvent()
   {
      m_delegate.Invoke(...);
   }       
}

... mas isso é um monte de código apenas para (mais ou menos) fazer o que os eventos do tipo campo já nos fornecem.


Também seria mais difícil para coisas como designers usarem ... você basicamente dependeria de convenções de nomenclatura para métodos, em vez de haver metadados públicos dizendo "isto é um evento".
Jon Skeet

3

Os eventos têm vantagens distintas em comparação aos campos de delegado. Os eventos podem ser definidos em interfaces em contraste com os campos, adicionando abstração ao código e, ainda mais importante: os eventos só podem ser chamados de dentro da classe de definição. No seu caso, qualquer pessoa poderia chamar o evento, possivelmente destruindo seu código.

Veja esta postagem do blog para mais informações.


2
Você não deveria comparar eventos e delegados - compare eventos e campos públicos com um tipo de delegado . E não, a estrutura não exige que os eventos tenham essa assinatura. Você pode criar um evento de qualquer tipo de delegado que desejar.
Jon Skeet

3

delegado é um tipo de referência. Ele herda MulticastDelegate . o evento é um modificador. eventoé um modificador especial para delegado. Ele modifica alguma acessibilidade de função / método, por exemplo, método Invoke Depois de modificada por evento modificador, uma instância de delegado se torna um novo conceito de "Evento". Portanto, o evento é apenas um delegado modificado. Você não pode alterar diretamente a referência ou invocar um evento fora da classe onde o evento foi definido, mas pode alterar a referência ou invocar uma instância de delegado normal. Os eventos fornecem proteção adicional, para que tenham mais recursos de segurança. Quando você está fora da classe onde o evento foi definido, você pode fazer dois tipos de operações no Evento, "+ =" e "- =". Mas você pode acessar todos os campos públicos, propriedades, métodos, etc. de uma instância de delegado normal. Aqui está um exemplo:

namespace DelegateEvent
{
    //the following line behave as a class. It is indeed a reference type
    public delegate void MyDelegate(string inputs);

    //The following line is illegal. It can only be an instance. so it cannot be directly under namespace
    //public event MyDelegate MyEvent;


    public class MyClassA
    {
        public event MyDelegate MyEventA;
        public MyDelegate MyDelegateA;


        System.Threading.ManualResetEvent MyResetEvent = new System.Threading.ManualResetEvent(false);
        public void TryToDoSomethingOnMyDelegateA()
        {
            if (MyDelegateA != null)
            {
                //User can assecc all the public methods.
                MyDelegateA("I can invoke detegate in classA");         //invoke delegate
                MyDelegateA.Invoke("I can invoke detegate in classA");  //invoke delegate
                IAsyncResult result = MyDelegateA.BeginInvoke("I can invoke detegate in classA", MyAsyncCallback, MyResetEvent);    //Async invoke
                //user can check the public properties and fields of delegate instance
                System.Reflection.MethodInfo delegateAMethodInfo = MyDelegateA.Method;

                MyDelegateA = testMethod;                   //reset reference
                MyDelegateA = new MyDelegate(testMethod);   //reset reference
                MyDelegateA = null;                         //reset reference


                MyDelegateA += testMethod;                  //Add delegate
                MyDelegateA += new MyDelegate(testMethod);  //Add delegate
                MyDelegateA -= testMethod;                  //Remove delegate
                MyDelegateA -= new MyDelegate(testMethod);  //Remove delegate
            }
        }

        public void TryToDoSomethingOnMyEventA()
        {
            if (MyEventA != null)
            {
                MyEventA("I can invoke Event in classA");           //invoke Event
                MyEventA.Invoke("I can invoke Event in classA");    //invoke Event
                IAsyncResult result = MyEventA.BeginInvoke("I can invoke Event in classA", MyAsyncCallback, MyResetEvent);      //Async invoke
                //user can check the public properties and fields of MyEventA
                System.Reflection.MethodInfo delegateAMethodInfo = MyEventA.Method;


                MyEventA = testMethod;                   //reset reference
                MyEventA = new MyDelegate(testMethod);   //reset reference
                MyEventA = null;                         //reset reference


                MyEventA += testMethod;                  //Add delegate
                MyEventA += new MyDelegate(testMethod);  //Add delegate
                MyEventA -= testMethod;                  //Remove delegate
                MyEventA -= new MyDelegate(testMethod);  //Remove delegate
            }
        }

        private void MyAsyncCallback(System.IAsyncResult result)
        {
            //user may do something here
        }
        private void testMethod(string inputs)
        {
            //do something
        }

    }
    public class MyClassB
    {
        public MyClassB()
        {
            classA = new MyClassA();
        }
        public MyClassA classA;
        public string ReturnTheSameString(string inputString)
        {
            return inputString;
        }


        public void TryToDoSomethingOnMyDelegateA()
        {
            if (classA.MyDelegateA != null)
            {
                //The following two lines do the same job --> invoke the delegate instance
                classA.MyDelegateA("I can invoke delegate which defined in class A in ClassB");
                classA.MyDelegateA.Invoke("I can invoke delegate which defined in class A in ClassB");
                //Async invoke is also allowed

                //user can check the public properties and fields of delegate instance
                System.Reflection.MethodInfo delegateAMethodInfo = classA.MyDelegateA.Method;

                classA.MyDelegateA = testMethod;                   //reset reference
                classA.MyDelegateA = new MyDelegate(testMethod);   //reset reference
                classA.MyDelegateA = null;                         //reset reference


                classA.MyDelegateA += testMethod;                  //Add delegate
                classA.MyDelegateA += new MyDelegate(testMethod);  //Add delegate
                classA.MyDelegateA -= testMethod;                  //Remove delegate
                classA.MyDelegateA -= new MyDelegate(testMethod);  //Remove delegate

            }

        }
        public void TryToDoSomeThingMyEventA()
        {
            //check whether classA.MyEventA is null or not is not allowed
            //Invoke classA.MyEventA is not allowed
            //Check properties and fields of classA.MyEventA is not allowed
            //reset classA.MyEventA reference is not allowed

            classA.MyEventA += testMethod;                  //Add delegate
            classA.MyEventA += new MyDelegate(testMethod);  //Add delegate
            classA.MyEventA -= testMethod;                  //Remove delegate
            classA.MyEventA -= new MyDelegate(testMethod);  //Remove delegate
        }

        private void testMethod(string inputs)
        {
            //do something here
        }
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.