A regra geral a seguir é que as estruturas devem ser coleções pequenas e simples (de um nível) de propriedades relacionadas, imutáveis após a criação; para qualquer outra coisa, use uma classe.
C # é bom, pois estruturas e classes não têm diferenças explícitas na declaração além da palavra-chave de definição; portanto, se você sentir que precisa "atualizar" uma estrutura para uma classe ou, inversamente, "fazer o downgrade" de uma classe para uma estrutura, é uma questão simples de alterar a palavra-chave (existem algumas outras dicas; as estruturas não podem derivar de qualquer outra classe ou tipo de estrutura, e eles não podem definir explicitamente um construtor sem parâmetros padrão).
Eu digo "principalmente", porque o mais importante a saber sobre estruturas é que, por serem tipos de valor, tratá-las como classes (tipos de referência) pode acabar meio doloroso. Particularmente, tornar mutáveis as propriedades de uma estrutura pode causar comportamento inesperado.
Por exemplo, digamos que você tenha uma classe SimpleClass com duas propriedades, A e B. Você instancia uma cópia dessa classe, inicializa A e B e passa a instância para outro método. Esse método modifica ainda mais A e B. De volta à função de chamada (a que criou a instância), os A e B da sua instância terão os valores dados a eles pelo método chamado.
Agora, você faz disso uma estrutura. As propriedades ainda são mutáveis. Você executa as mesmas operações com a mesma sintaxe de antes, mas agora os novos valores de A e B não estão na instância depois de chamar o método. O que aconteceu? Bem, sua classe agora é uma estrutura, o que significa que é um tipo de valor. Se você passar um tipo de valor para um método, o padrão (sem uma palavra-chave out ou ref) é passar "por valor"; uma cópia superficial da instância é criada para uso pelo método e, em seguida, destruída quando o método é concluído, deixando a instância inicial intacta.
Isso se torna ainda mais confuso se você tiver um tipo de referência como membro de sua estrutura (não é permitido, mas é uma prática extremamente ruim em praticamente todos os casos); a classe não seria clonada (apenas a referência da estrutura), portanto, as alterações na estrutura não afetariam o objeto original, mas as alterações na subclasse da estrutura afetarão a instância a partir do código de chamada. Isso pode facilmente colocar estruturas mutáveis em estados muito inconsistentes que podem causar erros muito longe de onde está o problema real.
Por esse motivo, praticamente toda autoridade em C # diz sempre tornar suas estruturas imutáveis; permita que o consumidor especifique os valores das propriedades apenas na construção de um objeto e nunca forneça meios para alterar os valores dessa instância. Campos somente leitura, ou propriedades de obtenção apenas, são a regra. Se o consumidor deseja alterar o valor, ele pode criar um novo objeto com base nos valores do antigo, com as alterações que deseja, ou pode chamar um método que fará o mesmo. Isso os obriga a tratar uma única instância de sua estrutura como um "valor" conceitual, indivisível e distinto de (mas possivelmente equivalente a) todos os outros. Se eles executarem uma operação em um "valor" armazenado por seu tipo, obterão um novo "valor" diferente do valor inicial,
Para um bom exemplo, veja o tipo DateTime. Você não pode atribuir nenhum dos campos de uma instância DateTime diretamente; você deve criar um novo ou chamar um método existente, o que produzirá uma nova instância. Isso ocorre porque uma data e hora são um "valor", como o número 5, e uma alteração no número 5 resulta em um novo valor que não é 5. Só porque 5 + 1 = 6 não significa que 5 agora é 6 porque você adicionou 1 a ele. DateTimes funcionam da mesma maneira; 12:00 não "se torna" 12:01 se você adicionar um minuto, em vez disso, obtém um novo valor 12:01 diferente de 12:00. Se esse é um estado lógico para o seu tipo (bons exemplos conceituais que não são incorporados ao .NET são Dinheiro, Distância, Peso e outras quantidades de uma UOM em que as operações devem levar em consideração todas as partes do valor), use uma estrutura e projete-a de acordo. Na maioria dos outros casos em que os subitens de um objeto devem ser mutáveis independentemente, use uma classe.