Pergunta

Eu estou usando C #, .NET 3.5. Eu entendo como utilizar eventos, como declará-los na minha classe, como ligá-los de algum outro lugar, etc. Um exemplo artificial:

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;
    }
}

No entanto, o que eu faço não entender, é quando se declara um manipulador de eventos

public EventHandler<EventArgs> ElementAddedEvent;

Nunca inicializado - assim o que, exatamente, é ElementAddedEvent? O que aponta para? O seguinte não vai funcionar, porque o EventHandler nunca é inicializado:

[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;
    }
}

Eu noto que há uma EventHandler.CreateDelegate (...), mas todas as assinaturas de método sugerem isso só é usado para prender delegados a um EventHandler já existente através da típica ElementAddedEvent + = new EventHandler (MyMethod).

Eu não tenho certeza se o Eu estou tentando fazer a vontade ajuda ... mas finalmente eu gostaria de chegar a um DataContext pai resumo em LINQ cujos filhos podem registar Tipos qual tabela eles querem "observou" que eu possa ter eventos como BeforeUpdate e AfterUpdate, mas específica para tipos. Algo parecido com isto:

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
    }
}

Thinking sobre isso me fez perceber que eu realmente não entendo o que está acontecendo sob a hod com os eventos - e eu gostaria de entender:)

Foi útil?

Solução

Eu escrevi isto em uma boa quantidade de detalhes em um artigo , mas aqui está o resumo, supondo que você está razoavelmente satisfeito com href="http://msdn.microsoft.com/en-us/library/ms173171.aspx" -se :

  • Um evento é apenas um "add" método e um método "remover", da mesma forma que uma propriedade é realmente apenas um método "get" e um método "set". (Na verdade, o CLI permite um "fogo raise /" método assim, mas C # não gera esse.) Metadados descreve o evento com referências aos métodos.
  • Quando você declarar um campo-like evento (como o seu ElementAddedEvent) o compilador gera os métodos e um campo privado (do mesmo tipo que o delegado). Dentro da classe, quando se referem a ElementAddedEvent você está se referindo ao campo. Fora da classe, você está se referindo ao campo.
  • Quando alguém se inscreve para um evento (com o operador + =) que chama o método add. Quando eles Baixa (com o operador - =). Que chama a remover
  • Para campo-like eventos, há um pouco de sincronização, mas caso contrário, a adicionar / remover apenas chamar Delegado. Combine / Retirar para alterar o valor do campo gerado automaticamente. Ambas estas operações atribuir ao campo de backup - lembre-se que os delegados são imutáveis. Em outras palavras, o código gerado automaticamente é muito parecido com isto:

    // 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);
            }
        }
    }
    
  • O valor inicial do campo gerado no seu caso é null -. E vai sempre se tornam null novamente se todos os assinantes são removidos, como é que o comportamento de Delegate.Remove

  • Se você quiser um manipulador de "no-op" para assinar o seu evento, de modo a evitar a verificação de nulidade, você pode fazer:

    public EventHandler<EventArgs> ElementAddedEvent = delegate {};
    

    O delegate {} é apenas um método anônimo que não se preocupa com seus parâmetros e não faz nada.

Se há alguma coisa que ainda não está claro, por favor, pergunte e eu vou tentar ajudar!

Outras dicas

Sob o capô, os eventos são apenas delegados com convenções de chamada especiais. (Por exemplo, você não tem que verificar se há nulidade antes de levantar um evento.)

Em pseudocódigo, Event.Invoke () divide assim:

Se evento tem Ouvintes Ligue para cada ouvinte sincronicamente sobre este tópico na ordem arbitrária.

Uma vez que os eventos são multicast, eles vão ter zero ou mais ouvintes, realizada em uma coleção. O loop CLR vontade através deles, chamando cada um em uma ordem arbitrária.

Uma grande limitação a lembrar é que os manipuladores de eventos executar no mesmo segmento como o evento é gerado. É um erro mental comum a pensar neles como gerando um novo segmento. Eles não.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top