Pergunta

Sendo essencialmente um desenvolvedor C ++ a ausência de RAII (Resource Acquisition Is Initialization) em Java e .NET sempre me incomodou. O fato de que o ônus da limpeza é movido do escritor classe para seu consumidor (por meio de try finally ou .NET da using construção ) parece ser marcadamente inferior.

Eu vejo por que em Java, não há suporte para RAII já que todos os objetos estão localizados na pilha e o coletor de lixo inerentemente não suporta destruição determinista, mas em .NET com a introdução de valores-tipos (struct), temos a (aparentemente) candidato perfeito para RAII. Um tipo de valor que é criado na pilha tem um âmbito de aplicação bem definido e C ++ semântica destructor pode ser utilizado. No entanto, o CLR não permite um tipo de valor para ter um destrutor.

As minhas buscas aleatórias encontrado um argumento que, se um tipo de valor é boxed cai sob a jurisdição do colector de lixo e, portanto, a sua destruição torna-se não-determinística. Eu sinto que este argumento não é suficientemente forte, os benefícios da RAII são grandes o suficiente para dizer que um tipo de valor, com um processo de destruição não pode ser encaixotado (ou usado como um membro da classe).

Para cortar uma longa história curta, minha pergunta é : existem outras razões tipos de valor não podem ser usados ??para introduzir RAII para .NET? (Ou você acha que meu argumento sobre vantagens óbvias de RAII são falhos?)

Editar: não devo ter formulado a pergunta claramente desde os primeiros quatro respostas ter perdido o ponto. I sei sobre Finalize e suas características não-deterministas, eu sei sobre a construção using e eu sinto essas duas opções são inferiores aos RAII. using é mais uma coisa que o consumidor de uma classe deve se lembrar (como muitas pessoas se esqueceram de colocar um StreamReader em um bloco using?). A minha pergunta é filosófica sobre o projeto de linguagem, porque é do jeito que é e pode ser melhorado?

Por exemplo, com um genérico deterministically destrutível valor do tipo I pode fazer as palavras-chave using e lock redundantes (alcançável por classes de biblioteca):

    public struct Disposer<T> where T : IDisposable
    {
        T val;
        public Disposer(T t) { val = t; }
        public T Value { get { return val; } }
        ~Disposer()  // Currently illegal 
        {
            if (val != default(T))
                val.Dispose();
        }
    }

Eu não posso deixar de final com uma citação a propósito que eu vi uma vez, mas atualmente não pode encontrar a sua origem.

Você pode tomar minha destruição determinista quando minha mão morta frio sai do escopo. - Anon

Foi útil?

Solução

Um título melhor seria "Por que não há RAII em C # / VB". C ++ / CLI (A evolução do aborto que foi administrado C ++) tem RAII no exacto mesmo sentido como C ++. É tudo o açúcar sintaxe apenas para o mesmo padrão de finalização que o resto das línguas CLI usar (Destructors em objetos gerenciados para C ++ / CLI são efetivamente finalisers), mas ele está lá.

Você pode gostar http://blogs.msdn.com /hsutter/archive/2004/07/31/203137.aspx

Outras dicas

Excelente pergunta e que tem me incomodado muito. Parece que os benefícios da RAII são percebidos de forma muito diferente. Na minha experiência com .NET, a falta de determinista coleção de recursos (ou pelo menos confiável) é um dos grandes inconvenientes. Na verdade, .NET obrigou-me várias vezes para empregar arquiteturas inteiras para lidar com recursos não gerenciados que pode (mas talvez não) requerem coleta explícita. Que, naturalmente, é uma desvantagem enorme, porque torna a arquitetura global mais difícil e dirige a atenção do cliente longe dos aspectos mais centrais.

Brian Harry tem um bom post sobre o rationales aqui .

Aqui está um trecho:

Que tal finalização e valor determinísticos tipos (estruturas)?

-------------- Eu vi um monte de perguntas sobre estruturas que têm destruidores, etc. Isso vale a pena Comente. Há uma variedade de questões de por que algumas línguas não tê-los.

(1) composição - Eles não dão a você vida determinista no geral caso para os mesmos tipos de composição razões descrito acima. Qualquer não-determinística classe que contém um não chamaria o destruidor até que foi finalizado pelo GC qualquer maneira.

(2) construtores de cópia - O único lugar onde seria realmente agradável está em empilhar os locais afectados. Eles seriam escopo para o método e tudo estaria ótimo. Infelizmente, a fim de obter isso realmente trabalho, você também tem que adicionar construtores de cópia e chamá-los cada vez que uma instância é copiado. Este é um dos mais feio e mais coisas complexas sobre C ++. Você acaba ficando código em execução em todo o lugar onde você não espera. isto faz com cachos de problemas de linguagem. Alguns designers de linguagem ter escolhido ficar longe deste.

Vamos dizer que criou estruturas com destruidores mas adicionado um grupo de restrições para fazer o seu comportamento sensato em face das questões acima. As restrições seria algo como:

(1) Você só pode declará-los como locais variáveis.

(2) Você só pode passá-los by-ref

(3) Não é possível atribuir-lhes, você só pode acessar campos e chamada métodos neles.

(4) Você não pode caixa eles.

(5) Problemas de usá-los através Reflexão (ligação tardia) porque isso geralmente envolve boxe.

talvez mais, mas isso é um bom começo.

O uso poderia ser isso? Seria você realmente criar um arquivo ou um classe de conexão do banco de dados que pode SOMENTE ser usado como uma variável local? Eu Não acredito que alguém realmente faria. O que você faria em vez disso é criar um conexão de propósito geral e, em seguida, criar uma auto destruído wrapper para usar como uma variável local com escopo. o chamador, então, escolher o que queria usar. Observe o chamador fez uma decisão e não é inteiramente encapsulado no próprio objeto. Dado que você poderia usar algo como as sugestões chegando em um par de seções.

O substituto para RAII em .NET é a usando-padrão, que funciona quase tão bem uma vez que você se acostumar com isso.

O mais próximo você chegar a esse é o operador stackalloc muito limitado.

Há alguns tópicos semelhantes se você procurar por eles, mas basicamente o que ferve para baixo é que se você quiser RAII em .NET simplesmente implementar um tipo IDisposable e utilizar a instrução "usando" para obter Eliminação determinista. Dessa forma, muitos dos mesmos ideoms podem ser implementados e utilizados em apenas uma maneira um pouco mais prolixo.

IMHO, as grandes coisas que VB.net e C # necessidade são:

  1. a declaração "Utilizar" para os campos, o que faria com que o compilador para gerar código para dispor de todos os campos, assim, com a tag. O comportamento padrão deve ser para o compilador para fazer uma classe implementar IDisposable se isso não acontecer, ou inserção lógica disposição antes do início da rotina de eliminação principal para qualquer um de uma série de padrões comuns de implementação IDisposal ou uso o mais um atributo para especificar que o material descarte deve ir em uma rotina com um nome específico.
  2. um meio de deterministically descarte de objetos cujos construtores e / ou inicializadores de campo lançar uma exceção, quer por um comportamento padrão (chamando o método de disposição padrão) ou um comportamento personalizado (chamar um método com um nome particular) .
  3. Para vb.net, um método de auto-gerado como nulo todos os campos withEvent.

Todos aqueles podem ser kludged muito bem em vb.net, e um pouco menos bem em C #, mas o suporte de primeira classe para eles melhorariam ambas as línguas.

Você pode fazer uma forma de RAII em .net e java usando métodos finalize (). A sobrecarga de finalize () é chamado antes que a classe é limpo pelo GC e assim pode ser usado para limpar todos os recursos que absolutamente não devem ser mantidos pela classe (exclusões mútuas, soquetes, identificadores de arquivo, etc.). Ainda não é determinista embora.

Com o .NET você pode fazer um pouco dessa forma determinística com a interface IDisposable e a palavra-chave usando, mas isso tem limitações (usando construção quando usado necessário para comportamento determinístico, desalocação de memória ainda não determinística, não é usado automaticamente nas aulas, etc. ).

E, sim, eu sinto que há um lugar para idéias RAII a ser introduzida no .NET e outras línguas gerenciados, embora o mecanismo exato poderia ser debatida interminavelmente. A única outra alternativa que eu poderia ver seria a introdução de um GC que pudesse lidar com a limpeza de recursos arbitrária (e não apenas a memória), mas então você tem problemas quando os referidos recursos absolutamente tem que ser lançado de forma determinística.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top