Właściwości .NET - użyć zestawu prywatnego lub właściwości tylko do odczytu?

45

W jakiej sytuacji powinienem użyć prywatnego zestawu właściwości, a nie uczynić z niej własność tylko do odczytu? Weź pod uwagę dwa bardzo uproszczone przykłady poniżej.

Pierwszy przykład:

Public Class Person

    Private _name As String

    Public Property Name As String
        Get
            Return _name
        End Get
        Private Set(ByVal value As String)
            _name = value
        End Set
    End Property

    Public Sub WorkOnName()

        Dim txtInfo As TextInfo = _
            Threading.Thread.CurrentThread.CurrentCulture.TextInfo

        Me.Name = txtInfo.ToTitleCase(Me.Name)

    End Sub

End Class

// ----------

public class Person
{
    private string _name;
    public string Name
    {
        get { return _name; }
        private set { _name = value; }
    }

    public void WorkOnName()
    {
        TextInfo txtInfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
        this.Name = txtInfo.ToTitleCase(this.Name);
    }
}

Drugi przykład:

Public Class AnotherPerson

    Private _name As String

    Public ReadOnly Property Name As String
        Get
            Return _name
        End Get
    End Property

    Public Sub WorkOnName()

        Dim txtInfo As TextInfo = _
            Threading.Thread.CurrentThread.CurrentCulture.TextInfo

        _name = txtInfo.ToTitleCase(_name)

    End Sub

End Class

// ---------------

public class AnotherPerson
{
    private string _name;
    public string Name
    {
        get { return _name; }
    }

    public void WorkOnName()
    {
        TextInfo txtInfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
        _name = txtInfo.ToTitleCase(_name);
    }
}

Oba dają takie same wyniki. Czy jest to sytuacja, w której nie ma dobra i zła, a to tylko kwestia preferencji?

tgxiii
źródło
public string Name { get; protected set; }poprzez dziedziczenie.
samis,

Odpowiedzi:

42

Istnieje kilka powodów, dla których warto skorzystać private set.

1) Jeśli w ogóle nie używasz pola kopii zapasowej i chcesz właściwości automatycznej tylko do odczytu:

public string Name { get; private set; }   

public void WorkOnName()
{
    TextInfo txtInfo = Thread.CurrentThread.CurrentCulture.TextInfo;
    Name = txtInfo.ToTitleCase(Name);
}  

2) Jeśli chcesz wykonać dodatkową pracę, gdy modyfikujesz zmienną w swojej klasie i chcesz uchwycić ją w jednym miejscu:

private string _name = string.Empty;
public string Name 
{ 
    get { return _name; }
    private set 
    {
        TextInfo txtInfo = Thread.CurrentThread.CurrentCulture.TextInfo;
        _name = txtInfo.ToTitleCase(value);
    }
}

Ogólnie rzecz biorąc, jest to kwestia osobistych preferencji. O ile mi wiadomo, nie ma powodów, by używać jednego nad drugim.

Adam Lear
źródło
1
Po prostu dodaję to, ponieważ pytanie ma również tag vb.net, ale w vb.net musisz określić backer, jeśli używasz prywatnego albo w get albo set. Więc w vb.net jest tak naprawdę mniej pracy, aby uczynić właściwość tylko do odczytu.
user643192
Nigdy o tym nie wiedziałem private set. :-)
Afzaal Ahmad Zeeshan
9
Aktualizacja dla tych, czytając tę odpowiedź w 2016. C # 6.0 wprowadzono tylko do odczytu automatyczne właściwości, które pozwalają mieć właściwość tylko do odczytu bez podkładu polu: public string Name { get; }. Jeśli nie chcesz modyfikowalnej właściwości, jest to teraz preferowana składnia.
Alexey
4
Jednym z bardzo dobrych powodów, aby nie używać, private setjest to, że nie jest tak niezmienne, jak lubimy udawać. Jeśli chcesz zaimplementować prawdziwie niezmienną klasę, tylko do odczytu jest koniecznością.
RubberDuck
Może być powodem wydajności NIE używać tylko do odczytu. Wydaje się powodować niepotrzebne kopiowanie struktur podczas uzyskiwania dostępu do metod pola strukturalnego tylko do odczytu. codeblog.jonskeet.uk/2014/07/16/…
Triynko
28

Użyj prywatnego zestawu, jeśli chcesz, aby setter nie był dostępny z zewnątrz .

Używaj tylko do odczytu, jeśli chcesz ustawić właściwość tylko raz . W konstruktorze lub inicjalizatorze zmiennych.

TESTUJ TO:

void Main()
{
    Configuration config = new Configuration();
    config.ResetConfiguration();

    ConfigurationReadOnly configRO = new ConfigurationReadOnly();
    configRO.ResetConfiguration();
}

public class Configuration
{
    public Color BackgroundColor { get; private set; }
    public Color ForegroundColor { get; private set; }
    public String Text { get; private set; }

    public Configuration()
    {
        BackgroundColor = Color.Black;
        ForegroundColor = Color.White;
        Text = String.Empty;
    }

    public void ResetConfiguration()
    {
        BackgroundColor = Color.Black;
        ForegroundColor = Color.White;
        Text = String.Empty;
    }
}

public class ConfigurationReadOnly
{
    public readonly Color BackgroundColor;
    public readonly Color ForegroundColor;
    public readonly String Text;

    public ConfigurationReadOnly()
    {
        BackgroundColor = Color.Black;
        ForegroundColor = Color.White;
        Text = String.Empty;
    }

    public void ResetConfiguration()
    {
        BackgroundColor = Color.Black; // compile error: due to readonly keyword
        ForegroundColor = Color.White; // compile error: due to readonly keyword
        Text = String.Empty; // compile error: due to readonly keyword
    }
}
asakura89
źródło
Chociaż zgadzam się z twoją odpowiedzią, twój przykład może przydać się do poprawy. Możesz skomentować miejsce wystąpienia błędu kompilatora.
Michael Richardson
Uwaga: Składnia VB.NET odpowiadająca readonlysłowu kluczowemu C # ma być stosowana ReadOnlydo pola zamiast do właściwości.
Zev Spitz
8

Czy mogę zasugerować trzecią opcję?

public class Person
{
    public string Name { get; protected set; }

    public void SetName(string name)
    {
        TextInfo txtInfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
        this.Name = txtInfo.ToTitleCase(name);
    }
}

To sprawia, że ​​właściwość Name skutecznie tylko do odczytu dla całego kodu zewnętrznego i zapewnia jawną metodę Set. Wolę jawny zestaw niż zwykłe użycie zestawu we właściwości Name, ponieważ zmieniasz wartość podczas ustawiania. Zwykle, jeśli ustawisz wartość właściwości, spodziewaj się, że odzyskasz tę samą wartość, kiedy zadzwonisz do get później, co nie zdarzyłoby się, gdybyś wykonał ToTitleCase w zestawie .

Jednak, jak powiedziałeś, nie ma jednej właściwej odpowiedzi.

Dave Wise
źródło
Uważam, że „zestaw prywatny” ma specjalną semantykę w kompilatorze (nie tylko jako prywatny akces). Czy to samo dotyczy chronionego zestawu? Jeśli nie, to gdzie jest semantyczny odpowiednik chronionego zestawu, jeśli prywatny zestaw ma specjalną semantykę? Nie znalazłem żadnej dokumentacji wyjaśniającej to.
Sprague,
1
+1, ale nazwałbym metodę „Zmień nazwę” zamiast „SetName”.
MattDavey,
4

Nie używaj drugiego przykładu. Cały sens korzystania z właściwości - nawet jeśli nie dzieje się nic poza pobieraniem gettera i ustawieniem setera - polega na przeprowadzeniu całego dostępu przez ten getter i seter, aby jeśli kiedykolwiek będziesz musiał zmienić zachowanie w przyszłości, wszystko jest w jedno miejsce.

Drugi przykład rezygnuje z tego w przypadku ustawiania właściwości. Jeśli zastosowałeś to podejście w dużej, złożonej klasie, a później musiałeś zmienić zachowanie nieruchomości, byłbyś w obszarze wyszukiwania i zamiany gruntów, zamiast zmiany w jednym miejscu - prywatnym seterze.

Carson63000
źródło
2

Ilekroć musiałem zmieniać poziom dostępu ustawiacza, generalnie zmieniałem go na Chroniony (tylko ta klasa i klasy pochodne mogą zmieniać wartość) lub Przyjaciel (tylko członkowie mojego zestawu mogą zmieniać wartość).

Ale korzystanie z trybu prywatnego ma sens, gdy chcesz wykonywać inne zadania w seterie oprócz zmiany wartości kopii zapasowej. Jak wskazano wcześniej, dobrym pomysłem jest, aby nie odwoływać się bezpośrednio do wartości kopii zapasowej, ale zamiast tego uzyskiwać do nich dostęp tylko poprzez ich właściwości. To gwarantuje, że późniejsze zmiany, które wprowadzisz we właściwości, zostaną zastosowane zarówno wewnętrznie, jak i zewnętrznie. Odwołanie do właściwości vs jej zmiennej bazowej praktycznie nie ma negatywnego wpływu na wydajność.

Prlaba
źródło
0

I praktycznie nie ma ograniczenia wydajności ...

Ale dla wyjaśnienia dostęp do właściwości jest wolniejszy niż dostęp do jej zmiennej pomocniczej. Moduł pobierający i ustawiający właściwości są metodami, które wymagają wywołania i zwrotu, natomiast zmienna pomocnicza właściwości jest dostępna bezpośrednio.

Dlatego w przypadkach, gdy do gettera właściwości można uzyskać wiele razy w bloku kodu, wartość właściwości jest czasami najpierw buforowana (zapisywana w zmiennej lokalnej) i zamiast niej używana jest zmienna lokalna. Oczywiście przy założeniu, że właściwości nie można zmienić asynchronicznie podczas wykonywania bloku.

Prlaba
źródło