Exceptioné o tipo base para todas as exceções e, como tal, terrivelmente inespecífico. Você nunca deve lançar essa exceção porque ela simplesmente não contém nenhuma informação útil. A captura do código de chamada para exceções não pode eliminar a ambigüidade da exceção lançada intencionalmente (de sua lógica) de outras exceções do sistema que são totalmente indesejadas e apontam falhas reais.
O mesmo motivo também se aplica a SystemException. Se você olhar a lista de tipos derivados, poderá ver um grande número de outras exceções com semânticas muito diferentes.
NullReferenceExceptione IndexOutOfRangeExceptionsão de um tipo diferente. Essas são exceções muito específicas, portanto, pode ser bom jogá-las . No entanto, você ainda não vai querer jogá-los, pois eles geralmente significam que existem alguns erros reais em sua lógica. Por exemplo, a exceção de referência nula significa que você está tentando acessar um membro de um objeto que é null. Se isso for uma possibilidade em seu código, você deve sempre verificar explicitamente nulle lançar uma exceção mais útil (por exemplo ArgumentNullException). Da mesma forma, IndexOutOfRangeExceptions ocorrem quando você acessa um índice inválido (em matrizes - não em listas). Você deve sempre certificar-se de não fazer isso em primeiro lugar e verificar os limites de, por exemplo, um array primeiro.
Existem algumas outras exceções como essas duas, por exemplo InvalidCastExceptionou DivideByZeroException, que são lançadas para falhas específicas em seu código e geralmente significam que você está fazendo algo errado ou não está verificando alguns valores inválidos primeiro. Ao descartá-los conscientemente de seu código, você está apenas tornando mais difícil para o código de chamada determinar se eles foram lançados devido a alguma falha no código ou apenas porque você decidiu reutilizá-los para algo em sua implementação.
Claro, existem algumas exceções (hah) a essas regras. Se você estiver construindo algo que pode causar uma exceção que corresponda exatamente a uma existente, sinta-se à vontade para usar isso, especialmente se estiver tentando corresponder a algum comportamento integrado. Apenas certifique-se de escolher um tipo de exceção muito específico.
Em geral, porém, a menos que você encontre uma exceção (específica) que preencha sua necessidade, você deve sempre considerar a criação de seus próprios tipos de exceção para exceções esperadas específicas. Especialmente quando você está escrevendo código de biblioteca, isso pode ser muito útil para separar as fontes de exceção.