As propriedades somente de gravação têm aplicações práticas?

Eu não sei porque comecei a pensar sobre isso, mas agora não consigo parar.

Em C # – e provavelmente em muitas outras linguagens, lembro que o Delphi costumava deixar você fazer isso também – é legal escrever esta syntax:

class WeirdClass { private void Hello(string name) { Console.WriteLine("Hello, {0}!", name); } public string Name { set { Hello(name); } } } 

Em outras palavras, a propriedade tem um setter mas não getter , é somente para gravação .

Eu acho que não consigo pensar em nenhuma razão para isso ser ilegal , mas eu nunca vi isso na natureza, e eu pareço um código muito shiny / horripilante na natureza. Parece um cheiro de código; parece que o compilador deveria estar me dando um aviso:

CS83417: O nome da propriedade parece ser completamente inútil e estúpido. Mau programador! Considere replace por um método.

Mas talvez eu não tenha feito isso por tempo suficiente, ou trabalhado em um campo muito restrito para ver qualquer exemplo do uso efetivo de tal construção.

Existem exemplos reais de propriedades somente de gravação que não podem ser substituídas por chamadas de método direto ou se tornariam menos intuitivas?

As propriedades somente de gravação são realmente muito úteis e eu as uso com frequência. É tudo sobre o encapsulamento – restringindo o access aos componentes de um object. Você geralmente precisa fornecer um ou mais componentes para uma class que precisa usar internamente, mas não há motivos para torná-los acessíveis a outras classs. Fazer isso apenas deixa a sua turma mais confusa (“eu uso este getter ou esse método?”), E é mais provável que sua class seja adulterada ou tenha seu propósito real ignorado.

Veja “Por que os methods getter e setter são maus” para uma discussão interessante sobre isso. Não sou tão hardcore quanto o escritor do artigo, mas acho que é bom pensar nisso. Eu normalmente uso setters mas raramente uso getters.

Minha primeira reação a essa pergunta foi: “E quanto ao método java.util.Random # setSeed ?”

Eu acho que as propriedades somente de gravação são úteis em vários cenários. Por exemplo, quando você não deseja expor a representação interna ( encapsulamento ), permitindo alterar o estado do object. java.util.Random é um bom exemplo de tal design.

A análise de código (também conhecida como FxCop) fornece um diagnóstico:

CA1044 : Microsoft.Design: Como a propriedade ‘WeirdClass.Name’ é somente gravação, inclua um getter de propriedade com uma acessibilidade maior ou igual ao seu configurador ou converta essa propriedade em um método.

Eu tenho código semelhante ao seguinte em um projeto XNA. Como você pode ver, a escala é somente escrita, é útil e (razoavelmente) intuitiva e uma propriedade de leitura ( get ) não faria sentido para ela. Claro que poderia ser substituído por um método, mas eu gosto da syntax.

 public class MyGraphicalObject { public double ScaleX { get; set; } public double ScaleY { get; set; } public double ScaleZ { get; set; } public double Scale { set { ScaleX = ScaleY = ScaleZ = value; } } // more... } 

Fazer algo somente para escrever é útil quando você não deve ler o que escreve.

Por exemplo, ao desenhar coisas na canvas (isso é precisamente o que o Gerenciador de Janelas da Área de Trabalho faz no Windows):
Você pode certamente desenhar em uma canvas, mas nunca deve precisar ler os dados (muito menos esperar obter o mesmo design de antes).

Agora, se as propriedades somente de gravação são úteis (em oposição aos methods), não sei com que frequência elas são usadas. Eu suponho que você poderia imaginar uma situação com uma propriedade “BackgroundColor”, onde escrever nela define a cor de fundo da canvas, mas a leitura não faz sentido (necessariamente).
Portanto, não tenho certeza sobre essa parte, mas, em geral, só queria salientar que há casos de uso para situações em que você apenas grava dados e nunca os lê.

Um uso para uma propriedade somente de gravação é oferecer suporte à injeção de dependência de setter, que é normalmente usada para parâmetros opcionais.

Digamos que eu tenha uma aula:

 public class WhizbangService { public WhizbangProvider Provider { set; private get; } } 

O WhizbangProvider não se destina a ser acessado pelo mundo exterior. Eu nunca iria querer interagir com o service.Provider , é muito complexo. Eu preciso de uma class como o WhizbangService para atuar como fachada. Ainda com o setter, eu posso fazer algo assim:

 service.Provider = new FireworksShow(); service.Start(); 

E o serviço inicia uma exibição de fogos de artifício. Ou talvez você prefira ver um show de água e luz:

 service.Stop(); service.Provider = new FountainDisplay(new StringOfLights(), 20, UnitOfTime.Seconds); service.Start(); 

E assim por diante….

Isso se torna especialmente útil se a propriedade for definida em uma class base. Se você escolher a injeção de construção para essa propriedade, precisará gravar uma sobrecarga de construtor em qualquer class derivada.

 public abstract class DisplayService { public WhizbangProvider Provider { set; private get; } } public class WhizbangService : DisplayService { } 

Aqui, a alternativa com injeção de construtor é:

 public abstract class DisplayService { public WhizbangProvider Provider; protected DisplayService(WhizbangProvider provider) { Provider = provider ?? new DefaultProvider(); } } public class WhizbangService : DisplayService { public WhizbangService(WhizbangProvider provider) : base(provider) { } } 

Esta abordagem é mais confusa na minha opinião, porque você precisa de alguns dos trabalhos internos da class, especificamente, que se você passar null para o construtor, você terá um padrão razoável.

Embora as diretrizes de design do .NET recomendem o uso de um método (“SetMyWriteOnlyParameter”) em vez de uma propriedade somente de gravação, considero úteis as propriedades somente de gravação ao criar objects vinculados a partir de uma representação serializada (de um database).

Nossa aplicação representa sistemas de produção em campo de petróleo. Temos o sistema como um todo (o object “Model”) e vários objects Reservoir, Well, Node, Group etc.

O modelo é criado e lido do database primeiro – os outros objects precisam saber a qual modelo pertencem. No entanto, o modelo precisa saber qual object inferior representa o total de vendas. Faz sentido que essas informações sejam armazenadas em uma propriedade Model. Se não quisermos fazer duas leituras de informações do Modelo, precisamos poder ler o nome do object Vendas antes de sua criação. Então, posteriormente, definimos a variável “SalesObject” para apontar para o object real (para que, por exemplo, qualquer alteração feita pelo usuário do nome desse object não cause problemas)

Preferimos usar uma propriedade somente de gravação – ‘SalesObjectName = “TopNode”‘ – em vez de um método – ‘SetSalesObjectName (“TopNode”) – porque parece que o último sugere que o SalesObject existe.

Este é um ponto menor, mas o suficiente para nos fazer querer usar uma propriedade somente de gravação.

No padrão MVP, é comum escrever uma propriedade com um setter na visualização (sem necessidade de um getter) – sempre que o apresentador definir seu conteúdo, a propriedade usará esse valor para atualizar algum elemento da interface do usuário.

Veja aqui uma pequena demonstração:

 public partial class ShowMeTheTime : Page, ICurrentTimeView { protected void Page_Load(object sender, EventArgs e) { CurrentTimePresenter presenter = new CurrentTimePresenter(this); presenter.InitView(); } public DateTime CurrentTime { set { lblCurrentTime.Text = value.ToString(); } } } 

O método InitView do apresentador simplesmente define o valor da propriedade:

 public void InitView() { view.CurrentTime = DateTime.Now; } 

Tanto quanto eu estou preocupado, eles não. Toda vez que eu usei uma propriedade somente de gravação como um hack rápido, mais tarde eu me arrependi. Normalmente acabo com um construtor ou uma propriedade completa.

Claro que estou tentando provar um negativo, então talvez haja algo que esteja perdendo.

Eu não consigo parar de pensar sobre isso também. Eu tenho um caso de uso para uma propriedade “somente gravação”. Eu não consigo ver uma boa saída disso.

Eu quero construir um atributo c # que deriva de AuthorizeAttribute para um aplicativo asp.net MVC. Eu tenho um serviço (digamos, IStore) que retorna informações que ajudam a decidir se o usuário atual deve ser autorizado. Injeção de Construtor não funcionará, porque

 public AllowedAttribute: AuthorizeAttribute { public AllowedAttribute(IStore store) {...} private IStore Store { get; set; } ... } 

torna store um parâmetro de atributo posicional, mas IStore não é um tipo de parâmetro de atributo válido e o compilador não compilará o código que é anotado com ele. Eu sou forçado a recorrer à Injeção de Setter de Propriedade.

 public AllowedAttribute: AuthorizeAttribute { [Inject] public IStore Store { private get; set; } ... } 

Juntamente com todas as outras coisas ruins sobre o Property Setter em vez de Constructor Injection, o serviço é uma propriedade somente de gravação. Ruim o suficiente para expor o setter a clientes que não precisem saber sobre os detalhes da implementação. Não faria qualquer favor a deixar os clientes verem o receptor também.

Eu acho que o benefício da injeção de dependência supera as diretrizes contra propriedades somente de gravação para este cenário, a menos que eu esteja faltando alguma coisa.

Não, posso imaginar qualquer caso em que não possam ser substituídos, embora possa haver pessoas que os considerem mais legíveis.

Caso hipotético:

 CommunicationDevice.Response = "Hello, World" 

ao invés de

 CommunicationDevice.SendResponse("Hello, World") 

O principal trabalho seria realizar efeitos colaterais de IO ou validação.

Curiosamente, o VB .NET ainda tem sua própria palavra-chave para esse tipo estranho de propriedade;)

 Public WriteOnly Property Foo() As Integer Set(value As Integer) ' ... ' End Set End Property 

mesmo que muitas propriedades “somente escritas” de fora realmente tenham um getter privado.

Eu trabalhei recentemente em um aplicativo que manipulava senhas. (Note que não estou afirmando que o seguinte é uma boa ideia; estou apenas descrevendo o que fiz.)

Eu tinha uma aula, HashingPassword , que continha uma senha. O construtor usou uma senha como argumento e a armazenou em um atributo privado. Com um desses objects, você pode adquirir um hash salgado para a senha ou verificar a senha em relação a um determinado hash salgado. Obviamente, não havia como recuperar a senha de um object HashingPassword .

Então eu tive outro object, não me lembro o que era; vamos fingir que era uma banana protegida por senha. A class Banana tinha uma propriedade somente de configuração chamada Password , que criava um HashingPassword partir do valor fornecido e armazenava-o em um atributo privado de Banana . Como o atributo de password do HashingPassword era privado, não havia como escrever um getter para essa propriedade.

Então, por que eu tenho uma propriedade set-only chamada Password vez de um método chamado SetPassword ? Porque isso fazia sentido. O efeito era, de fato, definir a senha do Banana , e se eu quisesse definir a senha de um object Banana , eu esperaria fazer isso definindo uma propriedade, não chamando um método.

Usando um método chamado SetPassword não teria tido grandes desvantagens. Mas também não vejo vantagens significativas.

Eu sei que isso está aqui há muito tempo, mas me deparei com isso e tenho um caso de uso válido (imho):

Quando você postar parâmetros em uma chamada webapi de ajax, você pode simplesmente tentar preencher as propriedades da class de parâmetros e include validação ou qualquer outra.

 public int MyFancyWepapiMethod([FromBody]CallParams p) { return p.MyIntPropertyForAjax.HasValue ? p.MyIntPropertyForAjax.Value : 42; } public class CallParams { public int? MyIntPropertyForAjax; public object TryMyIntPropertyForAjax { set { try { MyIntPropertyForAjax = Convert.ToInt32(value); } catch { MyIntPropertyForAjax = null; } } } } 

No lado do JavaScript, você pode simplesmente preencher os parâmetros, incluindo a validação:

 var callparameter = { TryMyIntPropertyForAjax = 23 } 

que é seguro neste exemplo, mas se você manipular userinput, pode não ter certeza se você tem um intvalue válido ou algo semelhante.

Bem, tecnicamente, não há nada de errado com uma propriedade somente do Setter por si. O Setter poderia fazer alguma validação, e talvez alguns outros methods – públicos ou privados – pudessem depender disso.

Por exemplo:

 class SomeClass { private int _someVar; SomeClass(int someVar) { if(someVar > 0) _someVar = someVar; } public int SomeVar { set { if(value > 0) _someVar = value; } } public string SomeFunction(){ return "This is an important function that uses _someVar " + _someVar; } } 

Então a declaração em si pode ser legal e válida. Seu corpo de método é um enorme cheiro de código, mas esse não é o trabalho de compiladores para se preocupar.