CodeGym /Cursos /C# SELF /Recomendações para organização do código

Recomendações para organização do código

C# SELF
Nível 54 , Lição 1
Disponível

1. Estilo de declaração e nomeação de eventos

Eventos — não são apenas delegates. São uma entidade separada para comunicação entre partes da aplicação, e a sua declaração deve ser clara.

Use o tipo de delegate correto

Em 99% dos casos use os delegates padrão:

  • EventHandler — para eventos sem dados.
  • EventHandler<TEventArgs> — quando precisa passar parâmetros.

A padronização facilita a manutenção do código e a integração com bibliotecas .NET. Não invente um delegate novo se EventHandler serve.


public event EventHandler SomethingHappened; // Sem dados
public event EventHandler<MyEventArgs> DataReceived; // Com dados adicionais

Se precisar de personalização especial — declare seu próprio delegate, mas isso é raro.

Nomeação de eventos

No .NET eventos são nomeados no tempo passado: Completed, Clicked, Changed, Received. Isso enfatiza que algo já aconteceu.

Exemplos:


public event EventHandler DataLoaded;    // Dados foram carregados
public event EventHandler<MessageEventArgs> MessageReceived; // Mensagem recebida
public event EventHandler Saving;        // O processo de salvar começou

Às vezes usa-se a forma Changing para eventos "antes" da mudança, dando chance de intervir.

2. Organização da classe publicadora: método virtual OnEvent

Sempre adicione um método protected virtual que invoque o evento: ponto central de chamada, extensibilidade por herança e comportamento previsível.


public class FileLoader
{
    public event EventHandler<FileLoadedEventArgs> FileLoaded;

    protected virtual void OnFileLoaded(FileLoadedEventArgs e)
    {
        FileLoaded?.Invoke(this, e);
    }

    public void Load(string filename)
    {
        // ... lógica de carregamento de arquivo ...
        OnFileLoaded(new FileLoadedEventArgs(filename));
    }
}

public class FileLoadedEventArgs : EventArgs
{
    public string FileName { get; }
    public FileLoadedEventArgs(string fileName) => FileName = fileName;
}

Deixe apenas OnFileLoaded chamar o evento — assim fica mais fácil manter e testar.

3. Regras de assinatura e descadastro: ciclo de vida, IDisposable

Se a vida do assinante for menor que a do publicador, obrigatoriamente desinscreva antes de destruir o assinante. É conveniente implementar IDisposable e desinscrever em Dispose().


public class TemporaryListener : IDisposable
{
    private readonly Publisher _publisher;

    public TemporaryListener(Publisher publisher)
    {
        _publisher = publisher;
        _publisher.DataReceived += HandleData;
    }

    private void HandleData(object sender, EventArgs e)
    {
        // Trabalho com os dados
    }

    public void Dispose()
    {
        _publisher.DataReceived -= HandleData;
    }
}

// Uso com using:
using (var listener = new TemporaryListener(myPublisher))
{
    // listener escuta eventos aqui
}
// Depois do using - Dispose é chamado, a desinscrição ocorreu

Se esquecer de desinscrever, o publicador manterá a referência ao delegate do assinante — você terá vazamento de memória e "objetos zumbi".

4. Chamada thread-safe de eventos

Em código multithread assinantes podem ser adicionados/removidos paralelamente à invocação do evento. Isso causa race conditions e NullReferenceException. Use o padrão thread-safe: copie o delegate para uma variável local.


protected virtual void OnSomethingHappened()
{
    EventHandler handler = SomethingHappened;
    handler?.Invoke(this, EventArgs.Empty);
}

Com C# 6+ é suficiente:


SomethingHappened?.Invoke(this, EventArgs.Empty);

5. Use EventArgs em vez de object

Não passe dados via object ou campos da classe. Use tipagem forte através de subclasses de EventArgs.


public class DownloadCompletedEventArgs : EventArgs
{
    public string FileName { get; }
    public long Size { get; }
    public DownloadCompletedEventArgs(string fileName, long size)
    {
        FileName = fileName;
        Size = size;
    }
}

public event EventHandler<DownloadCompletedEventArgs> DownloadCompleted;

6. Documentação de eventos e assinantes

Documente: quando o evento é chamado, o significado dos campos de EventArgs, se precisa desinscrever e quando.


/// <summary>
/// O evento ocorre após o carregamento bem-sucedido dos dados.
/// </summary>
public event EventHandler<DataLoadedEventArgs> DataLoaded;

7. Recomendações resumidas sobre arquitetura de eventos

Separe responsabilidades

O publicador apenas notifica sobre o fato. O assinante decide por si quando assinar e desassinar.

Evite "bombardear" com eventos

Não gere o mesmo evento dezenas de vezes por segundo sem necessidade — isso é carga excessiva.

Prefira não usar eventos para comunicação bidirecional

Eventos são para o padrão "um notifica — muitos escutam". Para comunicação bidirecional considere interfaces, callbacks ou outros mecanismos.

Não mantenha referências explícitas aos assinantes na classe

Não segure referências explícitas aos assinantes — eventos e delegates fazem isso automaticamente.

8. Antipadrões clássicos

Eventos sem tipagem


public event Action<object> SomethingHappened; // Não fica claro o que tem dentro

Ruim: tipagem quebrada, são necessários casts, perde-se a manutenibilidade.

Esquecer do descadastro


public class ShortLivedListener
{
    public ShortLivedListener(Publisher p) =>
        p.DataReceived += DoWork;

    private void DoWork(object sender, EventArgs e) { /* ... */ }
    // Sem Dispose, sem desinscrição => objetos zumbi!
}

Violação do SRP

A classe é ao mesmo tempo publicadora, assinante e handler — mistura de responsabilidades. Separe responsabilidades.

9. Aplicação prática em entrevistas e projetos

Em muitos projetos com publicação/assinatura a boa organização de eventos é chave para escalabilidade e manutenção. Em entrevistas geralmente pedem:

  • implementar um sistema de eventos com tipagem correta,
  • mostrar gerenciamento do ciclo de vida dos assinantes,
  • explicar a chamada thread-safe de eventos.

Código de eventos limpo, documentado e corretamente organizado já te destaca entre os candidatos.

2
Tarefa
C# SELF, nível 54, lição 1
Bloqueado
Organizando eventos com múltiplos assinantes
Organizando eventos com múltiplos assinantes
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION