.NET属性-使用私有集还是只读属性?


45

在什么情况下,我应该在属性上使用私有集而不是将其设置为ReadOnly属性?考虑以下两个非常简单的示例。

第一个例子:

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

第二个例子:

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

它们都产生相同的结果。这是没有对与错的情况,只是一个喜好问题吗?


public string Name { get; protected set; }通过继承。
samis

Answers:


42

有两个使用原因private set

1)如果您根本不使用后备字段,并且想要一个只读的自动属性:

public string Name { get; private set; }   

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

2)如果您要在修改类中的变量时想要做额外的工作,并想在一个位置捕获它,请执行以下操作:

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

通常,这是个人喜好问题。据我所知,没有性能上的理由要使用一种。


1
仅添加此内容是因为问题也具有vb.net标记,但是在vb.net中,如果在get或set上使用private,则需要指定支持者。因此,在vb.net中,我认为将属性设置为只读实际上要少一些工作。
user643192

我从来不知道private set。:-)
Afzaal Ahmad Zeeshan 2014年

9
针对2016年阅读此答案的人员的更新。C#6.0引入了只读自动属性,使您可以拥有不带后备字段的readonly属性:public string Name { get; }。如果您不希望使用可变属性,那么这就是现在的首选语法。
Alexey

4
不使用的一个很好的理由private set是,它不像我们想像的那样不变。如果要实现真正的不变类,则必须只读。
RubberDuck

出于性能原因,请勿使用只读。访问只读结构域的方法时,似乎导致结构的不必要复制。codeblog.jonskeet.uk/2014/07/16/...
Triynko

28

如果您不想从外部访问设置员,请使用私有设置

如果只想设置一次属性,则使用readonly。在构造函数或变量初始化程序中。

测试:

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

虽然我同意您的回答,但您的示例可能会有所改进。您可能要在可能发生编译器错误的地方进行注释。
迈克尔·理查森

注意:与C#readonly关键字相对应的VB.NET语法将应用于ReadOnly字段而不是属性。
Zev Spitz

8

我可以建议第三个选择吗?

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

这使Name属性有效地对所有外部代码只读,并提供了一个显式的Set方法。我更喜欢显式Set,而不是简单地在Name属性上使用set,因为在设置它时要更改它的值。一般来说,如果你设置一个属性值,你希望当您打电话,以获得相同的值回GET以后,如果你在做你的ToTitleCase这将不会发生

但是,正如您所说,没有一个正确的答案。


我相信“私有集”在编译器中具有特殊的语义(而不仅仅是充当私有访问器)。保护集也是如此吗?如果不是,如果私有集具有特殊语义,那么与保护集等效的语义在哪里?我没有找到任何解释此问题的文档。
斯普拉格

1
+1,但我将方法称为“重命名”而不是“ SetName”。
MattDavey 2012年


4

不要使用第二个示例。使用属性的全部目的-即使没有发生getter getting和setter设置的事情-都是通过该getter和setter集中所有访问,以便将来如果需要更改行为,一切都在一个地方。

您的第二个示例在设置属性的情况下放弃了这一点。如果您在大型的复杂类中使用该方法,并且后来需要更改属性的行为,那么您将位于搜索和替换的土地上,而不是在一个地方(即私有设置器)进行更改。


2

每当我需要更改设置器的访问级别时,通常都将其更改为“受保护”(仅此类和派生类可以更改值)或“朋友”(仅我程序集的成员可以更改值)。

但是,当您要在设置器中执行其他任务(除了更改支持值)时,使用“专用”是很有意义的。如前所述,最好不要直接引用您的支持值,而只能通过其属性访问它们。这样可以确保以后对属性所做的更改在内部和外部都适用。而且,引用属性与其后备变量几乎没有性能损失。


0

而且几乎没有性能损失...

但为了澄清,访问属性比访问其背衬变量慢。属性的getter和setter是需要Call和Return的方法,而属性的后备变量可以直接访问。

这就是为什么在某个代码块中可以多次访问属性的getter的情况,有时会首先缓存该属性的值(保存在局部变量中),然后使用局部变量代替。当然,这假定该属性在执行块时不能异步更改。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.