entendendo setters privados


90

Eu não entendo a necessidade de ter setters privados que começaram com C # 2.

Ter um método setter para mim é permitir que o usuário defina algumas variáveis ​​nessa classe. Ao fazer isso, não exporemos as variáveis ​​diretamente aos usuários. Em vez disso, permitimos que eles façam isso por meio desse método setter público.

Isso para mim está usando "encapsulamento". Existem alguns argumentos por aí que afirmam que setters privados permitem que você aplique o encapsulamento.

Não estou usando o encapsulamento usando métodos setter públicos? Por que precisamos de setters privados?

Qual é a diferença entre uma classe imutável e uma classe com configuradores privados?


1
Eu adorei as aulas particulares - me ajudou a reformular as aulas feias. Eles também tornam impossível a declarar e definir uma variável de instância não-constante de uma só vez assim: private File settingsFile = null;e, em seguida, em um dos construtores: if (settingsFile == null) { settingsFile = GetSettingsFile() };. Refatorar código assim me fazia chorar às vezes :). Só porque você pode definir um membro antes do construtor, não significa que você deva, pois, com vários construtores, isso torna difícil seguir a lógica. Os setters privados o forçam a definir valores dentro do construtor ou posteriormente.
Hamish Grubijan,

Respostas:


261

Logicamente.

A presença de um configurador privado ocorre porque você pode usar a propriedade automática:

public int MyProperty { get; set; }

O que você faria se quisesse torná-lo somente leitura?

public int MyProperty { get; }

Oh droga!! Não consigo acessá-lo em minha própria classe; Devo criá-lo como uma propriedade normal:

private int myProperty;
public int MyProperty { get { return myProperty; } }

Hmm ... mas perdi o recurso "Propriedade automática" ...

public int MyProperty { get; private set; }

AHHH .. isso é melhor !!


Obrigado. Isso faz sentido novamente
Dene,

3
@ktutnik Obrigado por explicar isso da maneira que você fez. Também faz sentido para mim agora!
Vivek M. Chawla

3
Resposta ilustrada de forma excelente.
imnk

3
Oh crap!! I can't access it from my own classA partir do C # 6.0, isso só é verdadeiro fora da fase de inicialização. Veja minha resposta stackoverflow.com/a/34223746/198797
tsemer

1
Adicionar à resposta de # tsemer, com c # 6, {get; }NÃO é equivalente a { get; private set; }. Para o primeiro caminho property.GetSetMethod(true)retorna nulle o último true. Isso me surpreendeu.
emragins

37

Um setter privado é útil se você tiver uma propriedade somente leitura e não quiser declarar explicitamente a variável de apoio.

Assim:

public int MyProperty
{
    get; private set;
}

é o mesmo que:

private int myProperty;
public int MyProperty
{
    get { return myProperty; }
}

Para propriedades não implementadas automaticamente, ele fornece uma maneira consistente de definir a propriedade de dentro de sua classe para que, se você precisar de validação, etc., você só a tenha em um lugar.

Para responder à sua pergunta final, o MSDN tem o seguinte a dizer sobre configuradores privados:

No entanto, para pequenas classes ou estruturas que apenas encapsulam um conjunto de valores (dados) e têm pouco ou nenhum comportamento, é recomendado tornar os objetos imutáveis ​​declarando o acessador set como privado.

Na página MSDN sobre propriedades implementadas automaticamente


1
Lamento, mas ainda não vejo o valor agregado por ter setters privados. Se não quisermos deixar o setter exposto, só temos o getter. Se quisermos adicionar um validador, podemos ter um setter público e adicionar validação a ele. Por que precisamos de um setter que não está acessível? A forma como eu entendo é como "Leve este carro, mas você não pode dirigir" por que você quer me dar o carro se eu não vou dirigi-lo de qualquer maneira
Dene,

@Dene - Você certamente pode fazer isso, pois não está errado. Ter propriedades implementadas automaticamente não é obrigatório.
ChrisF

Sei que não há nada de errado em fazer da maneira que expressei. É para mim apreciar o aprimoramento feito pelo C # 2. Parece haver muito exagero em torno dele, mas eu simplesmente não consigo sentir ou ver o valor.
Dene,

@Dene, eu perdi todo o hype sobre isso. Mas, quando finalmente vi que era possível fazer isso, fiquei feliz porque sabia como limpar algumas aulas longas da era .Net 1.1. Embora você provavelmente possa usar o valor da propriedade que ainda não foi definida, fazer isso é menos natural do que usar o valor de uma variável de membro de instância que foi definida como nula.
Hamish Grubijan,

Isso simplifica o código. Assim como propriedades de automóveis. Muitas das atualizações subsequentes do C # são para tornar o código mais conciso e, portanto, legível. Você ainda pode fazer as coisas de outra maneira se quiser - a compatibilidade com versões anteriores também parece ser um grande objetivo. Acho que é uma questão de gosto.
niico,

18

É bastante simples. Os configuradores privados permitem que você crie propriedades públicas ou protegidas somente leitura.

É isso aí. Essa é a única razão.

Sim, você pode criar uma propriedade somente leitura especificando apenas o getter, mas com as propriedades implementadas automaticamente, você deve especificar get e set, portanto, se quiser que uma propriedade implementada automaticamente seja somente leitura, você deve usar setters privados. Não há outra maneira de fazer isso.

É verdade que os setters privados não foram criados especificamente para propriedades somente leitura autoimplementadas, mas seu uso é um pouco mais esotérico por outras razões, em grande parte centrado em propriedades somente leitura e o uso de reflexão e serialização.


2
Obrigado "se você deseja que uma propriedade implementada automaticamente seja somente leitura, você deve usar configuradores privados". Isso faz sentido para mim
Dene,

17

Com a introdução do C # 6.0 e a sintaxe para inicializadores de propriedade automática , os configuradores privados não são mais necessários para propriedades que são definidas apenas na inicialização, seja inline ou dentro do construtor.

Essas novas sintaxes agora compilam:

Propriedade inicializada embutida

public class MyClass1 {
  public string MyProperty { get; } = "Aloha!"
}

Propriedade inicializada do construtor

public class MyClass2 {
  public string MyProperty { get; }

  public MyClass2(string myProperty) {
    MyProperty = myProperty;
  }
}

3
@Ziggler, na verdade não. Nem o OP pede isso. Ele simplesmente não entende a necessidade de tê-los. Isso responde: "você não precisa mais tê-los neste cenário".
tsemer

6

Eu não entendo a necessidade de ter setters privados que começaram com C # 2.

Por exemplo, a classe de fatura permite que o usuário adicione ou remova itens da propriedade Itens, mas não permite que o usuário altere a referência Itens (ou seja, o usuário não pode atribuir a propriedade Itens a outra instância do objeto da lista de itens).


public class Item
{
  public string item_code;
  public int qty;

  public Item(string i, int q)
  {
    this.item_code = i;
    this.qty = q;
  }
}

public class Invoice
{
  public List Items { get; private set; }

  public Invoice()
  {
    this.Items = new List();
  }
}

public class TestInvoice
{
  public void Test()
  {
    Invoice inv = new Invoice();
    inv.Items.Add(new Item("apple", 10));

    List my_items = new List();
    my_items.Add(new Item("apple", 10));

    inv.Items = my_items;   // compilation error here.
  }
}

+1 para destacar que as propriedades podem ser manipuladas por meio do getter público, mesmo que tenham um setter privado.
user1725145

4

Digamos, por exemplo, que você não armazene a variável real por meio da propriedade ou use o valor para calcular algo.

Nesse caso, você pode criar um método para fazer seu cálculo

private void Calculate(int value)
{
 //...
}

Ou você pode fazer isso usando

public int MyProperty {get; private set;}

Nesses casos, eu recomendaria usar o último, pois as propriedades refatoram cada elemento membro intacto.

Fora isso, digamos que você mapeie a propriedade com uma variável. Nesse caso, dentro do seu código, você deseja escrever assim:

public int myprop;
public int MyProperty {get { return myprop;}}

... ...

this.myprop = 30;

... ...
if(this.MyProperty > 5)
   this.myprop = 40;

O código acima parece horrível, pois o programador precisa sempre ter cuidado para usar MyProperty para Get e myprop para Set.

Para manter a consistência, você pode usar um setter privado que torna o Propoerty somente leitura fora, enquanto você pode usar seu setter interno em seu código.


3

Acho que algumas pessoas já dançaram em torno disso, mas para mim, o valor dos setters privados é que você pode encapsular o comportamento de uma propriedade, mesmo dentro de uma classe. Como abhishek observou, se você deseja disparar um evento de alteração de propriedade toda vez que uma propriedade é alterada, mas não deseja que uma propriedade seja lida / gravada para o público, você deve usar um configurador privado ou aumentar o evento em qualquer lugar que você modifique o campo de apoio. Este último está sujeito a erros porque você pode esquecer. Da mesma forma, se atualizar um valor de propriedade resulta em algum cálculo sendo executado ou outro campo sendo modificado, ou uma inicialização preguiçosa de algo, então você também desejará encerrar no setter privado em vez de ter que se lembrar de fazer isso em todos os lugares que fizer uso do campo de apoio.


2

Encapsulamento significa que o estado de um objeto só ocorre por meio de uma interface definida, e por isso a classe pode garantir que esse estado seja sempre válido e de acordo com o propósito da classe.

Em alguns casos, portanto, está perfeitamente de acordo com o princípio de encapsulamento apenas expor um campo publicamente - todos os valores possíveis para o campo são válidos com todos os outros valores possíveis de todos os outros campos e, portanto, o programador pode decidir ativamente permitir o campo para ser manipulado livremente por código externo.

No entanto, esses casos são restritos principalmente a classes que são, em sua maioria, "dados simples e antigos". Eles também não são muito interessantes a esse respeito, então chega de falar deles.

Em outros casos, em outras linguagens, haveria um método getter e setter, algo como int getId()obter um valor e void setId(int val)atualizá-lo.

As propriedades nos permitem usar a mesma sintaxe para ler e escrever por meio de métodos que usaríamos para ler e escrever um campo. Este é um bom açúcar sintático, embora não seja vital.

(Na verdade, devido à forma como a reflexão funciona e casos como DataBinder.Evalesse, pode ser útil ter uma propriedade mesmo quando um campo funcionaria bem, mas isso é outro assunto).

Até que os setters privados fossem introduzidos (na verdade, o que mudou com C # 2 é a sintaxe para ter um setter privado e um getter público ou protegido no mesmo bloco), poderíamos ter um método privado para fazer o trabalho do setter privado, então setters privados não são realmente necessários. Eles são úteis, portanto, embora sejam apenas açúcar sintático, eles são muito úteis.

O encapsulamento não é uma questão de se seus setters (ou getters) são públicos, privados, protegidos ou internos, mas uma questão de se eles são apropriados . Comece com um padrão de cada campo sendo privado (e nesse caso readonly) e então, conforme necessário, adicione membros (sejam propriedades ou métodos) que alteram esses campos e certifique-se de que o objeto permaneça válido conforme eles mudam . Isso garante que a invariante de uma classe seja mantida, o que significa que as regras que descrevem o conjunto válido de estados em que ela pode estar nunca são quebradas (os construtores também ajudam, certificando-se de que ela comece nesse estado válido).

Quanto à sua última pergunta, ser imutável significa que uma classe não tem setters públicos, protegidos ou internos e nenhum método público, protegido ou interno que altere quaisquer campos. Existem graus disso, em C #, existem três graus possíveis:

  1. Todos os campos de instância de uma classe são readonly, portanto, nem mesmo o código privado pode alterá-los. É garantido que seja imutável (qualquer coisa que tente alterá-lo não será compilado) e possivelmente otimizações podem ser feitas por trás disso.

  2. Uma classe é imutável de fora porque nenhum membro público muda nada, mas não é garantido pelo uso de readonlypara não ser alterado de dentro.

  3. Uma classe é imutável quando vista de fora, embora algum estado seja alterado como um detalhe de implementação. Por exemplo, um campo pode ser memoizado e, portanto, embora do lado de fora uma tentativa de obtê-lo apenas recupere o mesmo valor, a primeira tentativa realmente o calcula e, em seguida, o armazena para recuperação em tentativas subsequentes.


1

Você precisa de um configurador privado, se quiser apoiar o seguinte cenário (não só para isso, mas isso deve apontar um bom motivo): Você tem uma propriedade que é somente leitura em sua classe, ou seja, apenas a própria classe pode mudar ele, mas pode alterá-lo após construir a instância. Para ligações, você precisaria disparar um evento PropertyChanged, de preferência isso deve ser feito no configurador de propriedade (privada). Na verdade, você poderia simplesmente disparar o evento PropertyChanged de algum outro lugar na classe, mas usar o setter privado para isso é "boa cidadania", porque você não distribui seus disparadores de mudança de propriedade por toda a sua classe, mas os mantém no propriedade, a que pertence.


1

Sim, você está usando o encapsulamento usando propriedades, mas há mais nuances no encapsulamento do que apenas assumir o controle sobre como as propriedades são lidas e gravadas. Negar uma propriedade a ser definida de fora da classe pode ser útil tanto para robustez quanto para desempenho.

Uma classe imutável é uma classe que não muda depois de criada, então setters privados (ou nenhum setter) são necessários para proteger as propriedades.

Os configuradores privados passaram a ser usados ​​com mais frequência com a abreviação de propriedade que foi instruída em C # 3. No C # 2, o configurador foi frequentemente apenas omitido e os dados privados acessados ​​diretamente quando definidos.

Está Propriedade:

public int Size { get; private set; }

é o mesmo que:

private int _size;
public int Size {
  get { return _size; }
  private set { _size = value; }
}

exceto, o nome da variável de apoio é criado internamente pelo compilador, então você não pode acessá-lo diretamente.

Com a propriedade abreviada, o configurador privado é necessário para criar uma propriedade somente leitura, já que você não pode acessar a variável de apoio diretamente.


Se bem me lembro, você não poderia ter modificadores de acesso diferentes em gete setantes de C # 2.0. Além disso, acho que você está misturando 2.0 e 3.0, já que a abreviatura auto-implementada a que se refere era 3.0.
Anthony Pegram,

1

Eu não entendo a necessidade de ter setters privados que começaram com C # 2.

Exemplo de caso de uso:

Eu tenho uma instância de um objeto de aplicativo 'UserInfo'que contém uma propriedade SessionTokenIDV1que não desejo expor aos consumidores de minha classe.

Também preciso definir esse valor em minha classe.

Minha solução foi encapsular a propriedade conforme mostrado e tornar o setter privado para que eu possa definir o valor do token de sessão sem permitir que o código de instanciação também o defina (ou mesmo veja no meu caso)

public class UserInfo
{
   public String SessionTokenIDV1 { get; set; }

}


public class Example
{
  // Private vars
  private UserInfo _userInfo = new UserInfo();

  public string SessionValidV1
  {
    get { return ((_userInfo.SessionTokenIDV1 != null) && (_userInfo.SessionTokenIDV1.Length > 0)) ? "set" : "unset"; }
    private set { _userInfo.SessionTokenIDV1 = value; }
  }
}

Editar: Tag de código fixo Editar: exemplo com erros que foram corrigidos


-1

Créditos para https://www.dotnetperls.com/property .

configuradores privados são iguais aos campos somente leitura. Eles só podem ser definidos no construtor. Se você tentar configurar de fora, obterá um erro de tempo de compilação.

public class MyClass
{
    public MyClass()
    {
        // Set the private property.
        this.Name = "Sample Name from Inside";
    }
     public MyClass(string name)
    {
        // Set the private property.
        this.Name = name;
    }
    string _name;
    public string Name
    {
        get
        {
            return this._name;
        }
        private set
        {
            // Can only be called in this class.
            this._name = value;
        }
    }
}

class Program
{
    static void Main()
    {
        MyClass mc = new MyClass();
        Console.WriteLine(mc.name);

        MyClass mc2 = new MyClass("Sample Name from Outside");
        Console.WriteLine(mc2.name);
    }
}

Por favor, veja a captura de tela abaixo quando tentei configurá-lo de fora da classe.

insira a descrição da imagem aqui

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.