Uma exceção definida pelo usuário é qualquer classe definida no seu código que deriva de System.Exception. Ela está sujeita a todas as regras de herança de classes, mas, além disso, o compilador e o runtime da linguagem tratam essas classes de um jeito especial, permitindo que suas instâncias sejam lançadas e capturadas fora do fluxo normal de controle, como discutido no exercício exceptions. Exceções definidas pelo usuário podem ser usadas de todas as formas, assim como as exceções do runtime e da Microsoft Base Class Library.
Esse tratamento especial se aplica apenas a classes derivadas de Exception. Você não pode lançar instâncias de qualquer outro tipo.
Exceções definidas pelo usuário costumam ser usadas para carregar informações extras, como uma mensagem e outros dados relevantes, que ficam disponíveis para as rotinas de captura. Essas informações podem então ser registradas em logs, informadas ao usuário ou usadas em uma nova tentativa. System.Exception tem membros de dados convenientes e construtores adequados para guardar uma mensagem e a exceção "interna".
Por convenção, os nomes de classes de exceção terminam com "Exception", por exemplo MyTerribleException.
Embora usar exceções definidas pelo usuário para encapsular e melhorar exceções de terceiros seja um padrão comum, a recomendação geral é não usá-las de forma tão liberal no seu próprio código fora desse caso de uso. Isso é considerado um antipadrão. Há quem discorde dessa visão, e você pode ver os dois lados do argumento nesta publicação do Stack Exchange.
Este artigo é uma boa introdução às exceções definidas pelo usuário.
Como parte de suas orientações sobre criar e lançar exceções, a equipe do .NET recomenda que as exceções definidas pelo usuário tenham vários construtores de conveniência. Para a maioria dos casos, a combinação ilustrada abaixo é adequada.
public class IncompleteExerciseException : System.Exception
{
public IncompleteExerciseException() : base() { }
public IncompleteExerciseException(string message) : base(message) { }
public IncompleteExerciseException(string message, System.Exception inner) : base(message, inner) { }
}