O C # fornece a você "menos corda para se enforcar" que o C ++? [fechadas]

14

Joel Spolsky caracterizou C ++ como "corda suficiente para se enforcar" . Na verdade, ele estava resumindo "Effective C ++" de Scott Meyers:

É um livro que diz basicamente que C ++ é corda suficiente para se enforcar, e depois alguns quilômetros extras de corda e, em seguida, algumas pílulas suicidas disfarçadas de M & Ms ...

Eu não tenho uma cópia do livro, mas há indicações de que grande parte do livro está relacionada a armadilhas do gerenciamento de memória que parecem renderizadas em c # porque o tempo de execução gerencia esses problemas para você.

Aqui estão as minhas perguntas:

  1. O C # evita armadilhas que são evitadas no C ++ apenas por uma programação cuidadosa? Em caso afirmativo, em que grau e como são evitados?
  2. Existem novas armadilhas diferentes em C # que um novo programador de C # deve estar ciente? Se sim, por que eles não poderiam ser evitados pelo design do C #?
alx9r
fonte
10
Desde o FAQ : Your questions should be reasonably scoped. If you can imagine an entire book that answers your question, you’re asking too much.. Eu acredito que isso se qualifica como uma pergunta ... #
028
@Oded Você está se referindo à pergunta de título limitado por caracteres? Ou minhas 3 ou mais perguntas mais precisas no corpo da minha postagem?
Alx9r
3
Francamente - tanto o título quanto cada uma das "perguntas mais precisas".
Oded
3
Eu iniciei uma discussão meta sobre esta questão.
Oded
1
Com relação à sua 3ª pergunta agora excluída, a série Effective C # de Bill Wagner (agora com 3 livros) me ensinou mais sobre programação C # do que qualquer outra coisa que li sobre o assunto. A revisão de Martins do EC # está certa, pois nunca pode ser uma substituição direta do Effective C ++, mas ele está errado ao pensar que deveria. Quando você não precisar mais se preocupar com os erros fáceis , precisará passar para erros mais difíceis .
Mark Booth

Respostas:

33

A diferença fundamental entre C ++ e C # decorre de um comportamento indefinido .

Não tem nada a ver com o gerenciamento manual de memória. Nos dois casos, esse é um problema resolvido.

C / C ++:

No C ++, quando você comete um erro, o resultado é indefinido.
Ou, se você tentar fazer certos tipos de suposições sobre o sistema (por exemplo, excesso de número inteiro assinado), é provável que seu programa seja indefinido.

Talvez leia esta série de três partes sobre comportamento indefinido.

Isto é o que faz C ++ tão rápido - o compilador não precisa se preocupar com o que acontece quando as coisas dão errado, para que ele possa evitar a verificação de correção.

C #, Java, etc.

No C #, você tem a garantia de que muitos erros surgirão na sua cara como exceções, e você tem muito mais garantia sobre o sistema subjacente.
Essa é uma barreira fundamental para tornar o C # tão rápido quanto o C ++, mas também é uma barreira fundamental para tornar o C ++ seguro e facilita o trabalho de depuração e depuração.

Tudo o resto é apenas molho.

user541686
fonte
Todo o material indefinido é realmente definido pela implementação; portanto, se você exceder um número inteiro não assinado no Visual Studio, receberá uma exceção se tiver ativado os sinalizadores corretos do compilador. Agora eu sei que é disso que você está falando, mas não é um comportamento indefinido , é só que as pessoas geralmente não o procuram. (mesmo com um comportamento verdadeiramente indefinido como o operador ++, é bem definido por cada compilador). Você poderia dizer o mesmo com o C #, apenas existe uma implementação - bastante 'comportamento indefinido' se você executar em Mono - por exemplo. bugzilla.xamarin.com/show_bug.cgi?id=310
gbjbaanb
1
É realmente definido ou definido apenas pelo que a implementação .net atual na versão atual do Windows faz? Mesmo o comportamento indefinido do c ++ é totalmente definido se você o definir como o que o g ++ fizer.
Martin Beckett
6
Transbordar números inteiros não assinados não é UB. Ele está transbordando números inteiros assinados que são UB.
DeadMG
6
@gbjbaanb: Como o DeadMG disse - o excesso de número inteiro assinado é indefinido. É não definido pela implementação. Essas frases têm significados específicos no padrão C ++ e não são a mesma coisa. Não cometa esse erro.
user541686
1
@CharlesSalvia: Uh, como exatamente "C ++ facilita tirar proveito do cache da CPU" do que C #? E que tipo de controle o C ++ fornece sobre a memória que você não pode ter em C #?
user541686
12

O C # evita armadilhas que são evitadas no C ++ apenas por uma programação cuidadosa? Em caso afirmativo, em que grau e como são evitados?

A maioria faz, outras não. E, claro, faz alguns novos.

  1. Comportamento indefinido - A maior armadilha do C ++ é que há muita linguagem indefinida. O compilador pode literalmente explodir o universo quando você faz essas coisas, e tudo ficará bem. Naturalmente, isso é incomum, mas é bastante comum que seu programa funcione bem em uma máquina e, por uma boa razão, não funcione em outra. Ou pior, a ação sutilmente diferente. O C # possui alguns casos de comportamento indefinido em sua especificação, mas são raros e em áreas do idioma que raramente são percorridas. O C ++ tem a possibilidade de executar um comportamento indefinido toda vez que você faz uma declaração.

  2. Vazamentos de memória - Isso é menos preocupante para o C ++ moderno, mas para iniciantes e durante cerca de metade de sua vida útil, o C ++ tornou super fácil o vazamento de memória. O C ++ eficaz veio ao redor da evolução das práticas para eliminar essa preocupação. Dito isto, o C # ainda pode vazar memória. O caso mais comum que as pessoas encontram é a captura de eventos. Se você tiver um objeto e colocar um de seus métodos como manipulador de um evento, o proprietário desse evento precisará ser submetido à GC para que o objeto morra. A maioria dos iniciantes não percebe que o manipulador de eventos conta como referência. Também há problemas em não descartar recursos descartáveis ​​que podem vazar memória, mas esses não são tão comuns quanto os indicadores no C ++ pré-eficaz.

  3. Compilação - C ++ possui um modelo de compilação retardado. Isso leva a uma série de truques para jogar bem com ele e manter os tempos de compilação baixos.

  4. Strings - O C ++ moderno torna isso um pouco melhor, mas char*é responsável por ~ 95% de todas as violações de segurança antes do ano 2000. Para programadores experientes, eles se concentrarão std::string, mas ainda há algo a ser evitado e um problema em bibliotecas antigas / piores . E isso está rezando para que você não precise de suporte unicode.

E realmente, essa é a ponta do iceberg. A questão principal é que o C ++ é uma linguagem muito ruim para iniciantes. É bastante inconsistente, e muitas das velhas armadilhas realmente, muito ruins, foram tratadas com a mudança dos idiomas. O problema é que os iniciantes precisam aprender os idiomas com algo como C ++ eficaz. O C # elimina muitos desses problemas e torna o resto menos uma preocupação até que você avance no caminho do aprendizado.

Existem novas armadilhas diferentes em C # que um novo programador de C # deve estar ciente? Se sim, por que eles não poderiam ser evitados pelo design do C #?

Mencionei o evento "vazamento de memória". Este não é um problema de linguagem, mas o programador espera algo que a linguagem não possa fazer.

Outra é que o finalizador de um objeto C # não é tecnicamente garantido para ser executado pelo tempo de execução. Isso geralmente não importa, mas faz com que algumas coisas sejam projetadas de maneira diferente do que você poderia esperar.

Outra armadilha que eu já vi programadores é a semântica de captura de funções anônimas. Quando você captura uma variável, você captura a variável . Exemplo:

List<Action> actions = new List<Action>();
for(int x = 0; x < 10; ++x ){
    actions.Add(() => Console.WriteLine(x));
}

foreach(var action in actions){
    action();
}

Não faz o que ingenuamente é pensado. Isso imprime 1010 vezes.

Tenho certeza de que há muitos outros que estou esquecendo, mas a questão principal é que eles são menos difundidos.

Telastyn
fonte
4
Vazamentos de memória são coisa do passado, e assim é char*. Sem mencionar que você ainda pode vazar memória em C # muito bem.
DeadMG
2
Chamar modelos de "colar de cadeia glorificado" é um pouco demais. Modelos são realmente um dos melhores recursos do C ++.
Charles Salvia
2
@CharlesSalvia Claro, eles são o recurso realmente distintivo do C ++. E sim, isso talvez seja uma simplificação excessiva para o impacto da compilação. Mas eles afetam desproporcionalmente os tempos de compilação e o tamanho da saída, especialmente se você não for cuidadoso.
Telastyn
2
@deadMG certamente, embora eu diria que muitos dos truques de meta-programação de modelos usados ​​/ necessários em C ++ são ... melhor implementados através de um mecanismo diferente.
Telastyn 26/08/12
2
@Telastyn todo o ponto de type_traits é obter informações de tipo em tempo de compilação para que você pode usar esta informação para fazer coisas como modelos especializar ou funções de sobrecarga de maneiras específicas, utilizandoenable_if
Charles Salvia
10

Na minha opinião, os perigos do C ++ são um pouco exagerados.

O perigo essencial é este: enquanto o C # permite executar operações de ponteiro "inseguras" usando a unsafepalavra - chave, o C ++ (sendo principalmente um superconjunto de C) permite usar ponteiros sempre que lhe apetecer. Além dos perigos usuais inerentes ao uso de ponteiros (que são iguais a C), como vazamentos de memória, estouros de buffer, ponteiros pendentes, etc., o C ++ apresenta novas maneiras de você estragar seriamente as coisas.

Essa "corda extra", por assim dizer, sobre a qual Joel Spolsky estava falando , basicamente se resume a uma coisa: escrever aulas que gerenciam internamente sua própria memória, também conhecida como " Regra dos 3 " (que agora pode ser chamada de Regra de 4 ou regra de 5 em C ++ 11). Isso significa que, se você quiser escrever uma classe que gerencia internamente suas próprias alocações de memória, é necessário saber o que está fazendo ou o programa provavelmente falhará. É necessário criar cuidadosamente um construtor, construtor de cópias, destruidor e operador de atribuição, que é surpreendentemente fácil de errar, geralmente resultando em falhas bizarras no tempo de execução.

CONTUDO , na programação C ++ diária real, é muito raro escrever uma classe que gerencia sua própria memória; portanto, é enganoso dizer que os programadores de C ++ sempre precisam ser "cuidadosos" para evitar essas armadilhas. Normalmente, você estará fazendo algo mais como:

class Foo
{
    public:

    Foo(const std::string& s) 
        : m_first_name(s)
    { }

    private:

    std::string m_first_name;
};

Essa classe é bem parecida com o que você faria em Java ou C # - não requer gerenciamento explícito de memória (porque a classe da biblioteca std::stringcuida de tudo isso automaticamente), e nenhum material da "Regra de 3" é necessário desde o padrão O construtor de cópias e o operador de atribuição estão bem.

É somente quando você tenta fazer algo como:

class Foo
{
    public:

    Foo(const char* s)
    { 
        std::size_t len = std::strlen(s);
        m_name = new char[len + 1];
        std::strcpy(m_name, s);
    }

    Foo(const Foo& f); // must implement proper copy constructor

    Foo& operator = (const Foo& f); // must implement proper assignment operator

    ~Foo(); // must free resource in destructor

    private:

    char* m_name;
};

Nesse caso, pode ser complicado para os novatos obterem o construtor de atribuição, destruidor e cópia correto. Mas, na maioria dos casos, não há razão para fazer isso. O C ++ facilita muito evitar o gerenciamento manual de memória em 99% do tempo, usando classes de biblioteca comostd::string e std::vector.

Outro problema relacionado é o gerenciamento manual de memória de uma maneira que não leva em consideração a possibilidade de uma exceção ser lançada. Gostar:

char* s = new char[100];
some_function_which_may_throw();
/* ... */
delete[] s;

Se some_function_which_may_throw()realmente não lançar uma exceção, você é deixado com um vazamento de memória porque a memória alocada paras nunca mais ser recuperado. Mas, novamente, na prática, isso não é mais um problema pela mesma razão que a "Regra de 3" não é mais um problema. É muito raro (e geralmente desnecessário) gerenciar realmente sua própria memória com ponteiros brutos. Para evitar o problema acima, tudo o que você precisa fazer é usar um std::stringou std::vector, e o destruidor será automaticamente chamado durante o desenrolamento da pilha após a exceção ser lançada.

Portanto, um tema geral aqui é que muitos recursos do C ++ que eram não herdados do C, como inicialização / destruição automática, construtores de cópias e exceções, forçam o programador a ter um cuidado extra ao executar o gerenciamento manual de memória no C ++. Porém, novamente, isso é apenas um problema se você planeja gerenciar manualmente a memória, o que quase nunca é mais necessário quando você possui contêineres padrão e indicadores inteligentes.

Portanto, na minha opinião, enquanto o C ++ oferece muita corda extra, quase nunca é necessário usá-lo para se enforcar, e as armadilhas sobre as quais Joel estava falando são trivialmente fáceis de evitar no C ++ moderno.

Charles Salvia
fonte
Era a regra de três no C ++ 03 e agora é a regra de quatro no C ++ 11.
DeadMG
1
Você pode chamá-lo de "Regra de 5" para construtor de cópias, mover construtor, atribuição de cópia, atribuição de movimentação e destruidor. Mas a semântica de movimentos nem sempre é necessária apenas para o gerenciamento adequado de recursos.
Charles Salvia
Você não precisa de atribuição separada de movimentação e cópia. O idioma de copiar e trocar pode fazer os dois operadores em um.
DeadMG
2
Responde à pergunta Does C# avoid pitfalls that are avoided in C++ only by careful programming?. A resposta é "não realmente, porque é tão fácil de evitar as armadilhas Joel estava falando em C ++ moderno"
Charles Salvia
1
IMO, enquanto linguagens de alto nível como C # ou Java fornecem gerenciamento de memória e outras coisas que devem ajudá- lo, nem sempre fazem o que deveria. Você ainda acaba cuidando do design do código para não deixar vazamento de memória (que não é exatamente o que você chamaria em C ++). Pela minha experiência, acho ainda mais fácil gerenciar a memória em C ++ porque você sabe que os destruidores serão chamados e, na maioria dos casos, eles fazem a limpeza. Afinal, o C ++ possui indicadores inteligentes para casos em que o design não permite o gerenciamento eficiente da memória. C ++ é ótimo, mas não para manequins.
Pijusn 31/08/2012
3

Eu realmente não concordo. Talvez menos armadilhas que o C ++, como existia em 1985.

O C # evita armadilhas que são evitadas no C ++ apenas por uma programação cuidadosa? Em caso afirmativo, em que grau e como são evitados?

Na verdade não. Regras como a regra de três perderam significado enorme em C ++ 11 graças a unique_ptre shared_ptrsendo padronizado. Usar as classes Standard de maneira vagamente sensível não é "codificação cuidadosa", é "codificação básica". Além disso, a proporção da população de C ++ que ainda é suficientemente estúpida, desinformada ou ambas para fazer coisas como gerenciamento manual de memória é muito menor do que antes. A realidade é que os palestrantes que desejam demonstrar regras como essa precisam passar semanas tentando encontrar exemplos onde ainda se aplicam, porque as classes Standard cobrem praticamente todos os casos de uso imagináveis. Muitas técnicas eficazes de C ++ foram da mesma maneira - o caminho do dodo. Muitos dos outros não são realmente tão específicos de C ++. Deixe-me ver. Ignorando o primeiro item, os próximos dez são:

  1. Não codifique C ++ como se fosse C. Isso é realmente apenas senso comum.
  2. Restrinja suas interfaces e use o encapsulamento. OOP.
  3. Gravadores de código de inicialização em duas fases devem ser gravados na fogueira. OOP.
  4. Saiba o que são semânticas de valor. Isso é realmente específico de C ++?
  5. Restrinja suas interfaces novamente, desta vez de uma maneira ligeiramente diferente. OOP.
  6. Destruidores virtuais. Sim. Provavelmente ainda é válido. finale overrideajudaram a mudar esse jogo em particular para melhor. Faça seu destruidor overridee você garante um bom erro de compilador se herdar de alguém que não o destruiu virtual. Faça sua aulafinal e nenhum scrub ruim possa aparecer e herdar dela acidentalmente sem um destruidor virtual.
  7. Coisas ruins acontecem se as funções de limpeza falharem. Isso não é realmente específico para C ++ - você pode ver o mesmo conselho para Java e C # - e, bem, praticamente todas as linguagens. Ter funções de limpeza que podem falhar é simplesmente ruim e não há nada de C ++ ou mesmo POO nesse item.
  8. Esteja ciente de como a ordem do construtor influencia as funções virtuais. Hilariamente, em Java (atual ou passado), simplesmente chamaria incorretamente a função da classe Derived, que é ainda pior que o comportamento do C ++. Independentemente disso, esse problema não é específico para C ++.
  9. As sobrecargas do operador devem se comportar como as pessoas esperam. Não é realmente específico. Inferno, quase não é sobrecarregador de operador específico, o mesmo poderia ser aplicado a qualquer função - não dê um nome a ela e faça com que ela faça algo completamente pouco intuitivo.
  10. Atualmente, isso é considerado uma má prática. Todos os operadores de atribuição fortemente protegidos contra exceções lidam perfeitamente com a auto-atribuição, e a auto-atribuição é efetivamente um erro lógico do programa, e a verificação da auto-atribuição não vale o custo de desempenho.

Obviamente, eu não vou passar por todos os itens eficazes do C ++, mas a maioria deles está simplesmente aplicando conceitos básicos ao C ++. Você encontraria o mesmo conselho em qualquer linguagem de operador sobrecarregável orientada a objeto e com valor de tipo. Os destruidores virtuais são o único que é uma armadilha de C ++ e ainda é válida - embora, sem dúvida, com ofinal classe do C ++ 11, não seja tão válido quanto era. Lembre-se de que o C ++ efetivo foi escrito quando a idéia de aplicar o OOP e os recursos específicos do C ++ ainda era muito nova. Esses itens dificilmente são sobre as armadilhas do C ++ e mais sobre como lidar com a alteração do C e como usar o OOP corretamente.

Edit: As armadilhas do C ++ não incluem coisas como as armadilhas do malloc. Quero dizer, por exemplo, todas as armadilhas que você pode encontrar no código C, você também pode encontrar no código C # inseguro, o que não é particularmente relevante e, em segundo lugar, apenas porque o Padrão o define para interoperação não significa que o uso seja considerado C ++ código. O Padrão também define goto, mas se você escrever uma pilha gigante de bagunça de espaguete usando-o, considero que o seu problema, não o idioma. Há uma grande diferença entre "codificação cuidadosa" e "seguindo expressões básicas da linguagem".

Existem novas armadilhas diferentes em C # que um novo programador de C # deve estar ciente? Se sim, por que eles não poderiam ser evitados pelo design do C #?

usingé uma merda. Realmente faz. E não tenho ideia de por que algo melhor não foi feito. Além disso, Base[] = Derived[]e praticamente todo uso de Object, que existe porque os designers originais não perceberam o enorme sucesso que os modelos tiveram em C ++, e decidiram que "vamos deixar tudo herdar de tudo e perder toda a segurança de tipos" era a escolha mais inteligente . Eu também acredito que você pode encontrar algumas surpresas desagradáveis ​​em coisas como condições de corrida com os delegados e outras coisas divertidas. Depois, há outras coisas gerais, como os genéricos são horríveis em comparação com os modelos, a colocação realmente desnecessária de tudo em um classe outras coisas.

DeadMG
fonte
5
Porém, uma base de usuários instruída ou novas construções não diminuem a corda. São apenas soluções alternativas, para que menos pessoas acabem penduradas. Embora isso seja um bom comentário sobre o C ++ eficaz e seu contexto na evolução da linguagem.
Telastyn
2
Não. É sobre como vários itens do Effective C ++ são conceitos que podem ser aplicados igualmente a qualquer linguagem orientada a objetos com tipo de valor. E educar a base de usuários para codificar C ++ real em vez de C definitivamente diminui a corda que o C ++ fornece. Além disso, eu esperaria que novas construções de linguagem estejam diminuindo a corda. É sobre como apenas porque o Padrão C ++ define mallocnão significa que você deve fazê-lo, mais do que apenas porque você pode se prostituir gotocomo uma cadela significa que é uma corda com a qual você pode se enforcar.
DeadMG
2
Usar as partes C do C ++ não é diferente de escrever todo o seu código unsafeem C #, o que também é ruim. Eu também poderia listar todas as armadilhas da codificação de C # como C, se você quiser.
DeadMG
@DeadMG: então realmente a pergunta deveria ser "programador C ++ tem bastante corda para se enforcar, enquanto ele é um programador C"
gbjbaanb
"Além disso, a proporção da população de C ++ que ainda é suficientemente estúpida, desinformada ou ambas para fazer coisas como gerenciamento manual de memória é muito menor do que antes". Citação necessária.
precisa saber é
3

O C # evita armadilhas que são evitadas no C ++ apenas por uma programação cuidadosa? Em caso afirmativo, em que grau e como são evitados?

C # tem as vantagens de:

  • Não sendo compatível com C com versões anteriores, evitando assim uma longa lista de recursos de linguagem "ruins" (por exemplo, ponteiros brutos) que são sintaticamente convenientes, mas que agora são considerados de mau estilo.
  • Ter semântica de referência em vez de semântica de valor, o que torna pelo menos 10 dos itens efetivos em C ++ discutidos (mas introduz novas armadilhas).
  • Ter menos comportamento definido pela implementação que o C ++.
    • Em particular, em C ++ que codifica o carácter de char, string, etc é definido pela implementação. O cisma entre a abordagem do Windows para Unicode ( wchar_tpara UTF-16, charpara "páginas de código" obsoletas) e a abordagem * nix (UTF-8) causa grandes dificuldades no código de plataforma cruzada. C #, OTOH, garante que a stringé UTF-16.

Existem novas armadilhas diferentes em C # que um novo programador de C # deve estar ciente?

Sim: IDisposable

Existe um livro equivalente a "C ++ eficaz" para C #?

Há um livro chamado Effective C #, que é semelhante em estrutura ao Effective C ++ .

dan04
fonte
0

Não, C # (e Java) são menos seguros que C ++

C ++ é verificável localmente . Posso inspecionar uma única classe em C ++ e determinar que a classe não vaza memória ou outros recursos, supondo que todas as classes referenciadas estejam corretas. Em Java ou C #, é necessário verificar todas as classes referenciadas para determinar se ela requer finalização de algum tipo.

C ++:

{
   some_resource r(...);  // resource initialized
   ...
}  // resource destructor called, no leaks here

C #:

{
   SomeResource r = new SomeResource(...); // resource initialized
   ...
} // did I need to finalize that?  May I should have used 'using' 
  // (or in Java, a grotesque try/finally construct)?  No way to tell
  // without checking the documentation for SomeResource

C ++:

{
    auto_ptr<SomeInterface> i = SomeFactory.create(...);
    i->f(...);
} // automatic finalization and memory release.  A new implementation of
  // SomeInterface can allocate and free resources with no impact
  // on existing code

C #:

{
   SomeInterface i = SomeFactory.create(...);
   i.f(...);
   ...
} // Sure hope someone didn't create an implementation of SomeInterface
  // that requires finalization.  In C# and Java it is necessary to decide whether
  // any implementation could require finalization when the interface is defined.
  // If the initial decision is 'no finalization', then no future implementation  
  // can acquire any resource without creating potential leaks in existing code.
Kevin Cline
fonte
3
... é bastante trivial nos IDEs modernos determinar se algo herda de IDisposable. O principal problema é que você precisa saber usar auto_ptr(ou alguns de seus parentes). Essa é a corda proverbial.
Telastyn 26/08/12
2
@ Telastyn não, o ponto é que você sempre usa um ponteiro inteligente, a menos que você realmente saiba que não precisa de um. No C #, a instrução using é exatamente como a corda a que você está se referindo. (ou seja, em C ++ você tem que lembrar de usar um ponteiro inteligente, por que C # não é tão ruim, embora você tem que lembre-se sempre usar um usando instrução)
gbjbaanb
1
@gbjbaanb Porque o quê? 5% na maioria das classes C # são descartáveis? E você sabe que precisa descartá-los, se forem descartáveis. No C ++, todos os objetos são descartáveis. E você não sabe se sua instância específica precisa ser tratada. O que acontece com os ponteiros retornados que não são de uma fábrica? É sua responsabilidade limpá-los? Não deveria ser, mas às vezes é. E, novamente, apenas porque você sempre deve usar um ponteiro inteligente não significa que a opção de não deixar de existir. Especialmente para iniciantes, essa é uma armadilha significativa.
Telastyn 26/08/12
2
@Telastyn: Saber usar auto_ptré tão simples quanto saber usar IEnumerableou saber usar interfaces, ou não usar ponto flutuante para moeda ou algo assim. É uma aplicação básica do DRY. Ninguém que sabe o básico de como programar cometeria esse erro. Ao contrário using. O problema usingé que você precisa saber para todas as classes se é ou não descartável (e espero que nunca mude) e se não é descartável, você bane automaticamente todas as classes derivadas que possam ter que ser descartáveis.
DeadMG
2
Kevin: Uh, sua resposta não faz sentido. Não é culpa do C # que você esteja fazendo errado. Você não não dependem de finalizadores no código escrito corretamente C # . Se você possui um campo que possui um Disposemétodo, deve implementar IDisposable(da maneira 'adequada'). Se sua classe faz isso (que é o equivalente à implementação de RAII para sua classe em C ++) e você usa using(que é como os ponteiros inteligentes em C ++), tudo funciona perfeitamente. O finalizador é destinado principalmente a evitar acidentes - Disposeé responsável pela correção, e se você não o estiver usando, bem, a culpa é sua, não dos C #.
user541686
0

Sim 100% sim, pois acho impossível liberar memória e usá-la em C # (assumindo que seja gerenciado e você não entra no modo inseguro).

Mas se você sabe programar em C ++ que um número inacreditável de pessoas não sabe. Você está bem. Como as aulas de Charles Salvia realmente não gerenciam suas memórias, pois tudo é tratado em aulas de STL preexistentes. Eu raramente uso ponteiros. Na verdade, eu fui para projetos sem usar um único ponteiro. (O C ++ 11 facilita isso).

Quanto a erros de digitação, erros tolos e etc (ex: if (i=0)bc, a tecla ficou presa quando você pressionou == muito rapidamente) o compilador reclama, o que é bom, pois melhora a qualidade do código. Outro exemplo é o esquecimento breaknas instruções switch e não permitir que você declare variáveis ​​estáticas em uma função (que eu não gosto às vezes, mas é uma boa ideia).


fonte
4
Java e C # fez o =/ ==problema ainda pior, usando ==a igualdade de referência e introdução .equalspara a igualdade de valor. O programador ruim agora precisa acompanhar se uma variável é 'double' ou 'Double' e não se esqueça de chamar a variante correta.
kevin Cline
@kevincline +1, mas em C # structvocê pode fazer o ==que funciona incrivelmente bem, já que na maioria das vezes alguém só teria strings, ints e floats (ou seja, apenas membros struct). No meu próprio código, nunca recebo esse problema, exceto quando quero comparar matrizes. Eu não acho que eu jamais comparar tipos de lista ou não struct (string, int, float, DateTime, KeyValuePair e muitos outros)
2
O Python acertou usando ==a igualdade de valor e a igualdade isde referência.
Dan04 27/08/12
@ dan04 - Quantos tipos de igualdade você acha que o C # possui? Ver o excelente ACCU relâmpago Discussão: Alguns objetos são mais iguais do que outros
Mark Booth