Está lançando uma exceção de uma forma incorreta de propriedade?


14

Eu sempre soube que as propriedades (ou seja, suas operações de set / get) deveriam ser rápidas / imediatas e sem falhas. Você nunca deve tentar pegar ou configurar uma propriedade.

Mas estou procurando algumas maneiras de aplicar a segurança baseada em função nas propriedades de alguns objetos. Por exemplo, uma propriedade Employee.Salary. Algumas das soluções que encontrei e outras que tentaram (uma em particular é o exemplo da AOP aqui ) envolvem lançar uma exceção se o acessador não tiver as permissões corretas - mas isso vai contra uma regra pessoal que eu tive por um longo tempo agora.

Então eu pergunto: estou errado? As coisas mudaram? Foi aceito que as propriedades possam gerar exceções?


1
Essa mesma pergunta no StackOverflow recebeu mais atenção e, portanto, melhores respostas.
Roman Starkov

Respostas:


14

quando você define o valor de uma propriedade, é possível lançar uma exceção em um valor inválido

obter o valor de uma propriedade (quase) nunca deve gerar uma exceção

para acesso baseado em função, use interfaces / fachadas diferentes / mais burras; não deixe as pessoas verem coisas que não podem ter!


5

Considero que a maioria está em má forma, mas até a Microsoft recomenda o uso de exceções às vezes. Um bom exemplo é a propriedade abstrata Stream.Length . Conforme as diretrizes, eu estaria mais preocupado em evitar efeitos colaterais em getters e limitar efeitos colaterais em setters.


4

Definitivamente, eu argumentaria que há uma falha no design se você sentir a necessidade de lançar exceções de um criador de propriedades ou um criador de propriedades.

Uma propriedade é uma abstração que representa algo que é apenas um valor . E você deve poder definir um valor sem temer que isso possa gerar uma exceção. *

Se definir a propriedade resultar em um efeito colateral, isso realmente deve ser implementado como um método. E se não produzir efeitos colaterais, nenhuma exceção deve ser lançada.

Um exemplo já mencionado em uma resposta diferente é a Stream.Positionpropriedade Isso produz efeitos colaterais e pode gerar exceções. Mas esse configurador de propriedades é basicamente apenas um invólucroStream.Seek que você poderia chamar.

Pessoalmente, acredito que a posição não deveria ter sido uma propriedade gravável.

Outro exemplo em que você pode ser tentado a lançar uma exceção de um configurador de propriedades está na validação de dados:

public class User {
    public string Email {
        get { return _email; }
        set { 
            if (!IsValidEmail(value)) throw InvalidEmailException(value);
            _email = value;
        }
    }

Mas existe uma solução melhor para esse problema. Introduza um tipo representando um endereço de email válido:

public class Email {
    public Email(string value) {
        if (!IsValidEmail(value)) throw new InvalidEmailException(value);
        ...
    }
    ...
}

public class User {
    public Email Email { get; set; }
}

A Emailclasse garante que não possa conter um valor que não seja um endereço de email válido, e as classes que precisam armazenar emails ficam isentas do dever de validá-las.

Isso também leva a uma maior coesão (um indicador de bom design de software) - o conhecimento sobre o que é um endereço de e-mail e como é validado existe apenas no Email classe, que tem apenas essa preocupação.

* ObjectDisposedException é a única exceção válida (sem trocadilhos) que posso pensar no momento.


2

Sei que sua pergunta é específica ao .NET , mas como o C # compartilha um pouco da história com Java, pensei que você poderia estar interessado. Estou não de qualquer maneira o que implica que porque algo é feito em Java, isso deve ser feito em C #. Eu sei que os dois são muito diferentes, especialmente em como o C # tem um suporte de propriedades de linguagem muito aprimorado. Estou apenas dando algum contexto e perspectiva.

Na especificação JavaBeans :

Propriedades restritas Às vezes, quando ocorre uma alteração de propriedade, algum outro bean pode validar a alteração e rejeitá-la se for inapropriada. Nos referimos às propriedades que passam por esse tipo de verificação como propriedades restritas. No Java Beans, são necessários métodos de configuração de propriedade restrita para suportar a PropertyVetoException. Estes documentos para os usuários da propriedade restrita que tentaram atualizações podem ser vetados. Portanto, uma propriedade restrita simples pode se parecer com:

PropertyType getFoo();
void setFoo(PropertyType value) throws PropertyVetoException;

Leve tudo isso com um grão de sal, por favor. A especificação do JavaBeans é antiga e as Propriedades do C # são (IMO) uma grande melhoria em relação às propriedades baseadas em "convenção de nomenclatura" que o Java possui. Eu só estou tentando dar um pouco de contexto, é tudo!


É um bom argumento, mas a distinção entre um método setter e um ac # setter de propriedades é significativa o suficiente para ser um caso diferente para mim. Da mesma forma, as exceções verificadas do Java forçam o código de chamada a estar ciente de que uma exceção pode ser lançada; portanto, há menos chances de que isso pegue alguém de surpresa.
Steven Evers

2

O ponto de uma propriedade é o Princípio de Acesso Uniforme , ou seja, que um valor deve ser acessível através da mesma interface, implementada por armazenamento ou computação. Se sua propriedade está lançando uma exceção que representa uma condição de erro além do controle do programador, do tipo que deve ser capturado e manipulado, você está forçando seu cliente a saber que o valor é obtido via computação.

Por outro lado, não vejo nenhum problema em usar afirmações ou exceções semelhantes a afirmações que visam sinalizar o uso incorreto da API ao programador em vez de ser capturado e manipulado. Nesses casos, a resposta correta da perspectiva do usuário da API não é lidar com a exceção (importando-se assim implicitamente com a obtenção do valor por meio de computação ou armazenamento). É para corrigir seu código para que o objeto não termine em um estado inválido ou faça com que você corrija seu código para que a afirmação não seja acionada.


Não vejo como "você está forçando seu cliente a saber que o valor é obtido via computação" a seguir. Certamente, o acesso ao armazenamento também pode gerar uma exceção. De fato, como você definiria a diferença entre os dois?
Timwi

Acredito que a distinção que é feita é que simplesmente recuperar um valor de um campo (armazenamento) e fazer algo inócuo com ele, como colocá-lo em uma variável de um tipo apropriado (ou seja, sem vazamento), não pode lançar uma exceção. Nesse caso, uma exceção só poderia ocorrer se você subsequentemente fizesse algo com o valor. Se uma exceção estiver sendo lançada em um getter de propriedade, nesse caso, simplesmente o ato de recuperar o valor e colocá-lo em uma variável pode causar uma exceção.
Dr. Wily's Apprentice

1

AFAIK, essa orientação vem principalmente do processo de pensamento de que as propriedades podem acabar sendo usadas no tempo de design . (Por exemplo, a propriedade Text em um TextBox) Se a propriedade lançar uma exceção quando um designer estiver tentando acessá-lo, o VS não terá um bom dia. Você receberá vários erros ao tentar usar o designer e, para os designers da interface do usuário, eles não serão renderizados. Também se aplica ao tempo de depuração, embora o que você verá em um cenário de exceção seja apenas "xxx Exception" e não atrapalhe o VS IIRC.

Para os POCOs, isso realmente não fará nenhum mal, mas eu ainda evito fazer isso sozinho. Eu acho que as pessoas vão querer acessar propriedades com mais frequência, então normalmente devem ter um custo baixo. As propriedades não devem executar o trabalho dos métodos, devem apenas obter / definir algumas informações e concluir com elas como regra geral.


0

Embora muito dependa do contexto, eu geralmente evitaria colocar qualquer tipo de lógica quebrável (incluindo exceções) nas propriedades. Apenas como um exemplo, há muitas coisas que você (ou alguma biblioteca que você está usando) poderia fazer que usam reflexão para iterar sobre propriedades, resultando em erros que você não esperava no tempo de design.

Digo geralmente, embora possa haver momentos em que você absolutamente, definitivamente, deseja bloquear algo sem se preocupar muito com as consequências. Segurança, eu acho que seria o caso clássico lá.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.