Propiedades de .NET: ¿Usar conjunto privado o propiedad de solo lectura?

45

¿En qué situación debo usar un conjunto privado en una propiedad en lugar de convertirla en una propiedad de solo lectura? Tenga en cuenta los dos ejemplos muy simplistas a continuación.

Primer ejemplo:

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

Segundo ejemplo

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

Ambos producen los mismos resultados. ¿Es esta una situación en la que no hay bien ni mal, y es solo una cuestión de preferencia?

tgxiii
fuente
public string Name { get; protected set; }a través de la herencia.
samis

Respuestas:

42

Hay un par de razones para usar private set.

1) Si no está utilizando un campo de respaldo y desea una propiedad automática de solo lectura:

public string Name { get; private set; }   

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

2) Si desea hacer un trabajo extra cuando modifica la variable dentro de su clase y desea capturarla en una sola ubicación:

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

En general, sin embargo, es una cuestión de preferencia personal. Hasta donde sé, no hay razones de rendimiento para usar una sobre la otra.

Adam Lear
fuente
1
Solo agrego esto porque la pregunta también tiene una etiqueta vb.net, pero en vb.net debe especificar un respaldo si usa private en get o set. Entonces, en vb.net, en realidad es menos trabajo hacer que la propiedad sea de solo lectura, creo.
user643192
Nunca supe de eso private set. :-)
Afzaal Ahmad Zeeshan
10
Una actualización para los que están leyendo esta respuesta en 2016. C # 6.0 ha introducido sólo lectura auto-propiedades, que le permiten tener una propiedad de sólo lectura sin un campo respaldo: public string Name { get; }. Si no desea una propiedad mutable, esa es la sintaxis preferida ahora.
Alexey
44
Una muy buena razón para no usar private setes que no es tan inmutable como nos gusta pretender que lo es. Si desea implementar una clase verdaderamente inmutable, solo lectura es imprescindible.
RubberDuck
Puede ser una razón de rendimiento para NO usar solo lectura. Parece causar una copia innecesaria de estructuras al acceder a métodos de un campo de estructura de solo lectura. codeblog.jonskeet.uk/2014/07/16/…
Triynko
28

Utilice el conjunto privado cuando desee que no se pueda acceder al configurador desde afuera .

Use solo lectura cuando desee establecer la propiedad solo una vez . En el constructor o inicializador variable.

PRUEBE ESTO:

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
fuente
Si bien estoy de acuerdo con su respuesta, su ejemplo podría mejorar. Es posible que desee hacer un comentario donde se produciría el error del compilador.
Michael Richardson el
NB La sintaxis de VB.NET correspondiente a la readonlypalabra clave C # es aplicar ReadOnlyal campo en lugar de a la propiedad.
Zev Spitz
8

¿Puedo sugerir una tercera opción?

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

Esto hace que la propiedad Name sea de solo lectura efectiva para todo el código externo y proporciona un método Set explícito. Prefiero el conjunto explícito en lugar de simplemente usar el conjunto en la propiedad Nombre porque está cambiando el valor al configurarlo. Normalmente, si establece un valor de propiedad, espera recuperar el mismo valor cuando llame a get más adelante, lo que no sucedería si hiciera su ToTitleCase en el conjunto .

Sin embargo, como dijiste, no hay una respuesta correcta.

Dave Wise
fuente
Creo que 'conjunto privado' tiene una semántica especial en el compilador (no solo funciona como un descriptor de acceso privado). ¿Es este también el caso con el conjunto protegido? Si no, ¿dónde está el equivalente semántico al conjunto protegido si el conjunto privado tiene una semántica especial? No he podido encontrar ninguna documentación que explique esto.
Sprague
1
+1 pero llamaría al método "Rename" en lugar de "SetName".
MattDavey
4

No uses el segundo ejemplo. El objetivo de usar una propiedad, incluso si no sucede nada más allá de la obtención y la configuración del configurador, es canalizar todo el acceso a través de ese getter y setter para que si alguna vez necesita cambiar el comportamiento en el futuro, todo esté en un lugar.

Su segundo ejemplo lo abandona en el caso de establecer la propiedad. Si utilizó ese enfoque en una clase grande y compleja, y luego necesitó cambiar el comportamiento de la propiedad, estaría en la búsqueda y reemplazo de tierras, en lugar de hacer el cambio en un lugar: el setter privado.

Carson63000
fuente
2

Siempre que necesité cambiar el nivel de acceso de un setter, generalmente lo cambié a Protegido (solo esta clase y las clases derivadas pueden cambiar el valor) o Amigo (solo los miembros de mi asamblea pueden cambiar el valor).

Pero usar Private tiene mucho sentido cuando quieres hacer otras tareas en el setter además de cambiar el valor de respaldo. Como se señaló anteriormente, es un buen diseño no hacer referencia a sus valores de respaldo directamente, sino solo acceder a ellos a través de sus propiedades. Eso garantiza que los cambios posteriores que realice en una propiedad se apliquen tanto interna como externamente. Y prácticamente no hay penalización de rendimiento al hacer referencia a una propiedad frente a su variable de respaldo.

Prlaba
fuente
0

Y prácticamente no hay penalización de rendimiento ...

Pero para aclarar, acceder a una propiedad es más lento que acceder a su variable de respaldo. El captador y definidor de una propiedad son métodos que requieren una Llamada y un Retorno, mientras que se accede directamente a la variable de respaldo de una propiedad.

Es por eso que, en los casos en que se puede acceder al captador de una propiedad muchas veces dentro de un bloque de código, el valor de la propiedad a veces se almacena primero en caché (se guarda en una variable local) y se usa la variable local en su lugar. Por supuesto, eso supone que la propiedad no se puede cambiar de forma asincrónica mientras se ejecuta el bloque.

Prlaba
fuente