comprensione dei setter privati


90

Non capisco la necessità di avere setter privati ​​che hanno iniziato con C # 2.

Avere un metodo setter per me significa consentire all'utente di impostare alcune variabili in quella classe. In tal modo, non esporremo le variabili direttamente agli utenti. Invece li lasciamo fare attraverso questo metodo di setter pubblico.

Questo per me sta usando "l'incapsulamento". Ci sono alcuni argomenti là fuori che affermano che i setter privati ​​ti permetteranno di applicare l'incapsulamento.

Non sto utilizzando l'incapsulamento utilizzando metodi di impostazione pubblica? Perché abbiamo bisogno di setter privati?

Qual è la differenza tra una classe immutabile e una classe con setter privati?


1
Ho adorato i setter privati ​​alla grande - mi ha aiutato a riqualificare le classi brutte. Essi inoltre rendono impossibile dichiarare e impostare una variabile di istanza non costante in una volta in questo modo: private File settingsFile = null;e quindi in uno dei costruttori: if (settingsFile == null) { settingsFile = GetSettingsFile() };. Il refactoring del codice in questo modo mi ha fatto piangere a volte :). Solo perché puoi impostare un membro prima del costruttore, non significa che dovresti, poiché, con più costruttori, questo rende DIFFICILE seguire la logica. I setter privati ​​ti obbligano a impostare i valori all'interno del costruttore o successivi.
Hamish Grubijan

Risposte:


261

Logicamente.

La presenza di un setter privato è dovuta al fatto che puoi utilizzare la proprietà auto:

public int MyProperty { get; set; }

Cosa faresti se volessi renderlo di sola lettura?

public int MyProperty { get; }

Oh merda!! Non riesco ad accedervi dalla mia classe; Dovrei crearlo come una normale proprietà:

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

Hmm ... ma ho perso la funzione "Proprietà automatica" ...

public int MyProperty { get; private set; }

AHHH .. così va meglio !!


Grazie. Questo ha di nuovo senso
Dene

3
@ktutnik Grazie per averlo illustrato come hai fatto. Ha senso anche per me, adesso!
Vivek M. Chawla

3
Risposta ottimamente illustrata.
imnk

3
Oh crap!! I can't access it from my own classA partire da C # 6.0 questo è vero solo al di fuori della fase di inizializzazione. Vedere la mia risposta stackoverflow.com/a/34223746/198797
tsemer

1
L'aggiunta alla risposta di # tsemer, con c # 6, {get; }NON equivale a { get; private set; }. Per il primo modo property.GetSetMethod(true)ritorna nulle per il secondo true. Questo mi ha sorpreso.
emragini

37

Un setter privato è utile se si dispone di una proprietà di sola lettura e non si desidera dichiarare esplicitamente la variabile di supporto.

Così:

public int MyProperty
{
    get; private set;
}

equivale a:

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

Per le proprietà non implementate automaticamente, ti dà un modo coerente di impostare la proprietà dall'interno della tua classe in modo che se hai bisogno di convalida ecc., Hai solo un posto.

Per rispondere alla tua ultima domanda, MSDN ha questo da dire sui setter privati:

Tuttavia, per piccole classi o strutture che incapsulano solo un insieme di valori (dati) e hanno pochi o nessun comportamento, si consiglia di rendere gli oggetti immutabili dichiarando la funzione di accesso set come privata.

Dalla pagina MSDN sulle proprietà implementate automaticamente


1
Mi dispiace di non vedere ancora il valore aggiunto a causa dei setter privati. Se non vogliamo che il setter sia esposto, abbiamo solo il getter. Se vogliamo aggiungere un validatore, possiamo avere un setter pubblico e aggiungervi la convalida. Perché abbiamo bisogno di un setter non accessibile? Per come la vedo io è come "Prendi questa macchina ma non puoi guidarla" perché vuoi darmi la macchina se non la guiderò comunque
Dene

@Dene - Puoi certamente farlo, perché non è sbagliato. Non è obbligatorio avere proprietà implementate automaticamente.
ChrisF

So che non c'è niente di sbagliato nel fare il modo in cui l'ho espresso. È solo per me apprezzare il miglioramento apportato da C # 2. Sembra che ci sia molto clamore intorno ad esso, ma non riesco a sentirlo o vedere il valore.
Dene

@ Dene, mi sono perso tutto il clamore al riguardo. Ma, quando finalmente ho visto che era possibile farlo, ero felice perché sapevo come ripulire alcune lunghe lezioni dall'era .Net 1.1. Sebbene tu possa probabilmente utilizzare il valore della proprietà che non è stato ancora impostato, farlo è meno naturale rispetto all'utilizzo del valore di una variabile membro di istanza che è stata impostata su null.
Hamish Grubijan

Semplifica il codice. Proprio come le proprietà delle auto. Molti degli aggiornamenti successivi a C # riguardano la creazione di codice più conciso e quindi leggibile. Puoi ancora fare le cose nell'altro modo se lo desideri: anche la compatibilità con le versioni precedenti sembra essere un grande obiettivo. Immagino sia una questione di gusti.
niico

18

È piuttosto semplice. I setter privati ​​consentono di creare proprietà pubbliche o protette di sola lettura.

Questo è tutto. Questa è l'unica ragione.

Sì, è possibile creare una proprietà di sola lettura specificando solo il getter, ma con le proprietà autoimplementate è necessario specificare sia get che set, quindi se si desidera che una proprietà implementata automaticamente sia di sola lettura, è necessario utilizzare setter privati. Non c'è altro modo per farlo.

È vero che i setter privati ​​non sono stati creati specificatamente per proprietà di sola lettura implementate automaticamente, ma il loro uso è un po 'più esoterico per altri motivi, in gran parte incentrato sulle proprietà di sola lettura e sull'uso della riflessione e della serializzazione.


2
Grazie "se vuoi che una proprietà implementata automaticamente sia di sola lettura, devi usare setter privati". Questo ha senso per me
Dene

17

Con l'introduzione di C # 6.0 e la sintassi per gli inizializzatori di proprietà automatica , i setter privati ​​non sono più necessari per le proprietà impostate solo durante l'inizializzazione, inline o all'interno del costruttore.

Queste nuove sintassi ora vengono compilate:

Proprietà inizializzata inline

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

Proprietà inizializzata dal costruttore

public class MyClass2 {
  public string MyProperty { get; }

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

3
@ Ziggler, in effetti non lo fa. Né OP lo chiede. Semplicemente non capisce la necessità di averli. Questo risponde: "non è più necessario averli in questo scenario".
tsemer

6

Non capisco la necessità di avere setter privati ​​che hanno iniziato con C # 2.

Ad esempio, la classe della fattura consente all'utente di aggiungere o rimuovere elementi dalla proprietà Items ma non consente all'utente di modificare il riferimento Items (ovvero, l'utente non può assegnare la proprietà Items a un'altra istanza di oggetto dell'elenco di elementi).


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 per evidenziare che le proprietà possono essere manipolate tramite il getter pubblico, anche se hanno un setter privato.
user1725145

4

Supponiamo, ad esempio, di non memorizzare la variabile effettiva attraverso la proprietà o di utilizzare il valore per calcolare qualcosa.

In tal caso è possibile creare un metodo per eseguire il calcolo

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

Oppure puoi farlo usando

public int MyProperty {get; private set;}

In questi casi, consiglierei di utilizzare il successivo, poiché le proprietà effettuano il refactoring di ogni elemento membro intatto.

A parte questo, anche se dici di mappare la proprietà con una variabile. In tal caso, all'interno del tuo codice vuoi scrivere in questo modo:

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

... ...

this.myprop = 30;

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

Il codice sopra sembra orribile in quanto il programmatore deve sempre essere cauto nell'usare MyProperty per Get e myprop per Set.

Rether per coerenza puoi usare un setter privato che rende la proprietà di sola lettura all'esterno mentre puoi usare il suo setter all'interno del tuo codice.


3

Penso che alcune persone abbiano ballato intorno a questo, ma per me, il valore dei setter privati ​​è che puoi incapsulare il comportamento di una proprietà, anche all'interno di una classe. Come notato da abhishek, se si desidera attivare un evento di modifica della proprietà ogni volta che una proprietà viene modificata, ma non si desidera che una proprietà venga letta / scritta al pubblico, è necessario utilizzare un setter privato o aumentare il valore evento ovunque modifichi il campo sottostante. Quest'ultimo è soggetto a errori perché potresti dimenticarlo. In modo correlato, se l'aggiornamento del valore di una proprietà comporta l'esecuzione di un calcolo o la modifica di un altro campo o un'inizializzazione pigra di qualcosa, allora vorrai anche avvolgerlo nel setter privato piuttosto che dover ricordarti di farlo ovunque tu faccia utilizzo del backing field.


2

Incapsulamento significa che lo stato di un oggetto avviene solo attraverso un'interfaccia definita e per questo motivo la classe può assicurarsi che questo stato sia sempre valido e in linea con lo scopo della classe.

In alcuni casi, quindi, è perfettamente in linea con il principio di incapsulamento esporre pubblicamente un campo: tutti i valori possibili per il campo sono validi con tutti gli altri valori possibili di tutti gli altri campi, e quindi il programmatore può decidere attivamente di consentire il campo essere manipolato liberamente da codice esterno.

Questi casi sono però per lo più limitati a classi che sono per lo più "semplici vecchi dati". Inoltre non sono molto interessanti a questo proposito, quindi basta parlare di loro.

In altri casi, in altre lingue, si avrebbe un metodo getter e setter, qualcosa come int getId()ottenere un valore e void setId(int val)aggiornarlo.

Le proprietà ci consentono di utilizzare la stessa sintassi per la lettura e la scrittura attraverso i metodi che useremmo per leggere e scrivere un campo. Questo è un buon zucchero sintattico, anche se non vitale.

(In realtà, a causa del modo in cui funziona la riflessione e casi come DataBinder.Evalpuò essere utile avere una proprietà anche quando un campo funzionerebbe bene, ma questa è un'altra questione).

Fino all'introduzione dei setter privati ​​(in realtà, ciò che è cambiato con C # 2 è la sintassi per avere un setter privato e un getter pubblico o protetto nello stesso blocco), potremmo avere un metodo privato per fare il lavoro del setter privato, quindi i setter privati ​​non sono realmente necessari. Sono utili però, quindi mentre sono solo zucchero sintattico, sono piuttosto utili.

L'incapsulamento non è una questione se i tuoi setter (o getter) siano pubblici, privati, protetti o interni, ma una questione se sono appropriati . Inizia con un valore predefinito in cui ogni campo è privato (e del restoreadonly ) e quindi, se necessario, aggiungi membri (proprietà o metodi) che alterano quei campi e assicurati che l'oggetto rimanga valido mentre cambiano . Ciò garantisce che l' invariante di una classe sia mantenuta, il che significa che le regole che descrivono l'insieme di stati valido in cui può trovarsi non vengono mai violate (i costruttori aiutano anche assicurandosi che inizi in tale stato valido).

Per quanto riguarda la tua ultima domanda, essere immutabile significa che una classe non ha setter pubblici, protetti o interni e nessun metodo pubblico, protetto o interno che modifichi alcun campo. Ci sono gradi di questo, in C # ci sono tre gradi possibili:

  1. Tutti i campi di istanza di una classe lo sono readonly, quindi anche il codice privato non può modificarlo. È garantito che sia immutabile (tutto ciò che tenta di cambiarlo non verrà compilato) e possibilmente le ottimizzazioni possono essere fatte sul retro di questo.

  2. Una classe è immutabile dall'esterno perché nessun membro pubblico cambia nulla, ma non è garantito dall'uso di readonlynon essere modificata dall'interno.

  3. Una classe è immutabile come vista dall'esterno, sebbene alcuni stati siano modificati come dettagli di implementazione. Ad esempio, un campo potrebbe essere memorizzato, e quindi mentre dall'esterno un tentativo di ottenerlo recupera solo lo stesso valore, il primo tentativo del genere lo calcola effettivamente e quindi lo memorizza per il recupero nei tentativi successivi.


1

Hai bisogno di un setter privato, se vuoi supportare il seguente scenario (non solo per questo, ma questo dovrebbe indicare una buona ragione): Hai una proprietà che è di sola lettura nella tua classe, cioè solo la classe stessa può cambiare esso, ma può cambiarlo dopo aver costruito l'istanza. Per i binding dovresti quindi attivare un evento PropertyChanged, preferibilmente questo dovrebbe essere fatto nel setter di proprietà (privato). In realtà, potresti semplicemente attivare l'evento PropertyChanged da qualche altra parte della classe, ma usare il setter privato per questa è "buona cittadinanza", perché non distribuisci i tuoi trigger di modifica della proprietà in tutta la tua classe, ma tienili al proprietà, a cui appartiene.


1

Sì, stai usando l'incapsulamento usando le proprietà, ma ci sono più sfumature nell'incapsulamento che semplicemente prendere il controllo su come le proprietà vengono lette e scritte. Negare una proprietà da impostare dall'esterno della classe può essere utile sia per la robustezza che per le prestazioni.

Una classe immutabile è una classe che non cambia una volta creata, quindi sono necessari setter privati ​​(o nessun setter) per proteggere le proprietà.

I setter privati ​​sono diventati più frequenti con la scorciatoia di proprietà che è stata introdotta in C # 3. In C # 2 il setter veniva spesso omesso e ai dati privati ​​si accedeva direttamente quando impostati.

Questa proprietà:

public int Size { get; private set; }

equivale a:

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

tranne che il nome della variabile di supporto viene creato internamente dal compilatore, quindi non è possibile accedervi direttamente.

Con la proprietà shorthand il setter privato è necessario per creare una proprietà di sola lettura, poiché non è possibile accedere direttamente alla variabile di supporto.


Se ricordo bene, non era possibile avere modificatori di accesso diversi su gete setprima di C # 2.0. Inoltre, penso che tu stia mescolando 2.0 e 3.0, poiché l'abbreviazione implementata automaticamente a cui ti riferisci era 3.0.
Anthony Pegram

1

Non capisco la necessità di avere setter privati ​​che hanno iniziato con C # 2.

Esempio di caso d'uso:

Ho un'istanza di un oggetto applicazione 'UserInfo'che contiene una proprietà SessionTokenIDV1che non desidero esporre ai consumatori della mia classe.

Ho anche bisogno della capacità di impostare quel valore dalla mia classe.

La mia soluzione era incapsulare la proprietà come mostrato e rendere privato il setter in modo da poter impostare il valore del token di sessione senza consentire al codice di istanziazione di impostarlo (o anche di vederlo nel mio 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; }
  }
}

Modifica: modifica tag a codice fisso: l'esempio conteneva errori che sono stati corretti


-1

Crediti a https://www.dotnetperls.com/property .

i setter privati ​​sono gli stessi dei campi di sola lettura. Possono essere impostati solo nel costruttore. Se provi a impostare dall'esterno ottieni un errore in fase di compilazione.

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);
    }
}

Si prega di vedere la schermata di seguito quando ho provato a impostarlo dall'esterno della classe.

inserisci qui la descrizione dell'immagine

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.