Quando criar uma exceção personalizada em C #

9

Estou escrevendo uma classe para fazer interface com um simples dispositivo de hardware através de uma porta COM. O dispositivo pode ser configurado para usar vários modos, para que minha classe tenha uma SetOperatingModefunção que aceite um enumtipo UsbDeviceMode. Parece algo como isto:

class UsbDevice
{
    public void SetOperatingMode(UsbDeviceMode mode)
    { 
     byte[] buffer = new byte[4];
     buffer[0] = 0x5A;
     buffer[1] = 0x02;
     buffer[2] = (byte)mode;
     buffer[3] = 0x00; //IO_TYPE is always 0 in this case.

     _port.Write(buffer, 0, 4);
     int read = _port.Read(buffer, 0, 2);
     bool successfulSet = (read == 2 && buffer[0] == 0xFF && buffer[1] == 0x00);
    }
}

enum UsbDeviceMode
{
  IO_MODE = 0x00,
  IO_CHANGE = 0x10,
  I2C_S_20KHZ = 0x20,
  I2C_S_50KHZ = 0x30,
  I2C_S_100KHZ = 0x40,
  I2C_S_400KHZ = 0x50,
  I2C_H_100KHZ = 0x60,
  I2C_H_400KHZ = 0x70,
  I2C_H_1000KHZ = 0x80,
  SPI_MODE = 0x90,
  SERIAL = 0x01
};

Existe a possibilidade distinta de que esta operação possa falhar devido a vários motivos: A porta COM pode não existir mais, o dispositivo pode ter bloqueado ou falhou ou, por qualquer motivo, a operação falhou.

Uma falha seria inesperada, mas não incomum. Existem dois modos distintos de falhas: A porta COM gera uma exceção ( TimeoutExceptione InvalidOperationExceptionsão as esperadas). Ou eu poderia ler de volta um indicador de falha do dispositivo.

De qualquer forma, se SetOperatingMode()falhar, o dispositivo ou a comunicação será interrompida de alguma forma, e essa classe não poderá fazer nada a respeito.

Eu tenho 2 perguntas:

  1. Devo "pré-lançar" o InvalidOperationExceptionse a porta estiver fechada? A partir da documentação MSDN, SerialPort.Writee SerialPortleitura vai jogar se a porta está fechada. Eu posso verificar isso no topo da função, ou eu posso simplesmente deixá- _port.Write()la jogar.
  2. Deve haver um tipo de exceção totalmente novo lançado quando successfulSeté false? Se successfulSetfor false, não há nada que essa classe possa fazer. Deve haver algum tipo de SetOperatingModeFailedExceptionexceção para distinguir entre a falha da porta COM ou o dispositivo? Parece bastante demorado criar uma classe de exceção inteira apenas para esse ponto.
CurtisHx
fonte

Respostas:

10

Use uma exceção personalizada quando desejar que os usuários possam distinguir programaticamente entre determinadas condições de erro. Se essa situação não existir, você poderá lançar uma exceção mais "geral" e evitar a criação da classe de exceção personalizada.

No caso específico do seu SetOperatingMode()exemplo, a menos que você precise chamar maneiras específicas pelas quais essa chamada de método pode falhar, é melhor usar uma exceção mais geral. Em outras palavras, se sua intenção é lançar uma SetOperatingModeFailedExceptionexceção como possível resultado da chamada SetOperatingMode(), mas não distinguir programaticamente que tipo de falha no modo operacional ocorreu, você pode dispensar a criação de uma exceção personalizada (já que é a única que pode ser jogado) e simplesmente jogue um InvalidOperationException, que provavelmente é a exceção existente mais próxima.

Se você ainda deseja criar uma exceção personalizada, crie uma que possa ser reutilizada em diferentes métodos, como OperationFailedException.

Robert Harvey
fonte
2

Criar uma classe é fácil. Não é um processo demorado. O código de depuração que oculta os problemas é difícil. E demorado.

Ao decidir criar uma exceção ou não, a pergunta que você deve se fazer é "esse comportamento é normal, o comportamento esperado ou é excepcional"?

Nesse caso, o comportamento esperado é que o modo operacional esteja sempre definido. Portanto, eu sugeriria que exceções precisam ser lançadas. Eu permitiria quaisquer exceções da operação de gravação. Eu também criaria um SetOperatingModeFailedException se a última linha revelar que ocorreu um erro.

Nesse caso, seu método realmente tem a responsabilidade de tentar definir o modo de operação. Não tem a responsabilidade de gerenciar a conexão. Isso é responsabilidade de outra pessoa e, se não tiver sido feito corretamente, uma exceção deve ser lançada.

Stephen
fonte
Você parece ter entendido mal o que ele está perguntando. Ele deveria usar um dos tipos de exceção existentes ou criar o seu próprio?
Robert Harvey
1
Na verdade, eu respondi suas perguntas. "Eu permitiria quaisquer exceções da operação de gravação". (ou seja, não se preocupe em verificar se a porta está aberta) e "crie uma SetOperatingModeFailedException".
Stephen
1
Pfft. Uma exceção personalizada não é necessária. Que outra exceção o método pode lançar, exceto a que você escolheu? Como SetOperatingModeFailedException adiciona qualquer valor além, digamos, de InvalidOperationException? Nenhuma exceção informa por que a falha ocorreu ou oferece uma maneira de responder programaticamente com base no tipo de exceção lançada. E a criação de novas classes de exceção não é gratuita .
Robert Harvey
1
Que valor isso acrescenta? Ele agrega muito valor quando documentado corretamente. Diz que esse problema específico aconteceu. Não está claro que o código de chamada não possa se recuperar desse erro.
Stephen
Você pode fornecer uma mensagem de erro detalhada em qualquer exceção que lançar. A menos que você planeje usar exceções específicas para fornecer controle programático ao chamador, acho que criar uma exceção personalizada é uma perda de tempo. Não aceite minha palavra, leia isto , que diz "Crie suas próprias exceções definidas pelo usuário se desejar que os usuários possam distinguir programaticamente entre algumas condições de erro".
Robert Harvey