Essa é uma pergunta muito boa, pois já vi vários desenvolvedores de C ++ escreverem códigos C # terríveis.
É melhor não pensar em C # como "C ++ com sintaxe melhor". São linguagens diferentes que requerem abordagens diferentes em seu pensamento. O C ++ obriga você a pensar constantemente sobre o que a CPU e a memória farão. C # não é assim. O C # foi especificamente projetado para que você não esteja pensando na CPU e na memória e, em vez disso, no domínio comercial para o qual está escrevendo .
Um exemplo disso que eu vi é que muitos desenvolvedores de C ++ gostariam de usar loops porque são mais rápidos que o foreach. Geralmente, é uma má idéia no C # porque restringe os tipos possíveis de coleção que estão sendo iterados (e, portanto, a reutilização e flexibilidade do código).
Eu acho que a melhor maneira de reajustar de C ++ para C # é tentar abordar a codificação de uma perspectiva diferente. No início, isso será difícil, porque ao longo dos anos você estará acostumado a usar o segmento "o que a CPU e a memória estão fazendo" em seu cérebro para filtrar o código que você escreve. Mas em C #, você deve pensar sobre os relacionamentos entre os objetos no domínio comercial. "O que eu quero fazer" em oposição a "o que o computador está fazendo".
Se você quiser fazer algo em tudo na lista de objetos, em vez de escrever um loop for na lista, crie um método que IEnumerable<MyObject>
use e use um foreach
loop.
Seus exemplos específicos:
Controlando a vida útil dos recursos que requerem limpeza determinística (como arquivos). É fácil usá-lo em mãos, mas como usá-lo corretamente quando a propriedade do recurso está sendo transferida [... entre os threads]? Em C ++, eu simplesmente usava ponteiros compartilhados e deixava cuidar da 'coleta de lixo' na hora certa.
Você nunca deve fazer isso em C # (principalmente entre threads). Se você precisar fazer algo em um arquivo, faça-o em um local ao mesmo tempo. Não há problema (e, de fato, é uma boa prática) escrever uma classe de wrapper que gerencia o recurso não gerenciado que é passado entre suas classes, mas não tente passar um Arquivo entre threads e tenha classes separadas para gravar / ler / fechar / abrir. Não compartilhe a propriedade de recursos não gerenciados. Use o Dispose
padrão para lidar com a limpeza.
Luta constante com funções de substituição de genéricos específicos (eu amo coisas como especialização parcial de modelos em C ++). Devo abandonar todas as tentativas de executar qualquer programação genérica em c #? Talvez os genéricos sejam limitados de propósito e não seja c # -ish usá-los, exceto por um domínio específico de problemas?
Os genéricos em C # foram projetados para serem genéricos. Especializações de genéricos devem ser tratadas por classes derivadas. Por que um List<T>
comportamento deve ser diferente se é um List<int>
ou um List<string>
? Todas as operações List<T>
são genéricas, de modo a serem aplicadas a qualquer uma List<T>
. Se você deseja alterar o comportamento do .Add
método em a List<string>
, crie uma classe derivada MySpecializedStringCollection : List<string>
ou uma classe composta MySpecializedStringCollection : IList<string>
que use o genérico internamente, mas faça as coisas de maneira diferente. Isso ajuda você a evitar violar o Princípio da Substituição de Liskov e ferir os outros que usam sua classe.
Funcionalidade semelhante a macro. Embora geralmente seja uma péssima idéia, para alguns domínios de problemas, não há outra solução alternativa (por exemplo, avaliação condicional de uma instrução, como nos logs que devem apenas ir para as versões de depuração). Não tê-los significa que eu preciso colocar mais se (condição) {...} clichê e ainda não é igual em termos de desencadear efeitos colaterais.
Como já foi dito, você pode usar comandos de pré-processador para fazer isso. Os atributos são ainda melhores. Em geral, os atributos são a melhor maneira de lidar com coisas que não são a funcionalidade "principal" de uma classe.
Em resumo, ao escrever C #, lembre-se de que você deve pensar inteiramente no domínio comercial e não na CPU e na memória. Você pode otimizar mais tarde, se necessário , mas seu código deve refletir os relacionamentos entre os princípios de negócios que você está tentando mapear.
C#
para gerar outro código. Você pode ler umCSV
ou umXML
ou o que você arquivo como entrada e gerar umC#
ou umSQL
arquivo. Isso pode ser mais poderoso do que usar macros funcionais.