你应该私有财产吗?


19
private string mWhatever;

private string Whatever
{
    get
    {
        return this.mWhatever;
    }
    set
    {
        this.mWhatever = value;
    }
}

我见过有人为每个成员(无论是否私有)都进行财产管理……这有意义吗?当您想控制对包含它的类中的成员的访问时,有时可以看到它在1%的情况下是有意义的,因为如果您不对每个成员使用属性,则会导致不一致并检查是否成员是否具有访问权限(因为您可以在该类的范围内访问这两者)。

Answers:


17

简短的回答:是的,有需要时。否则,请使用自动实现的属性获取器和设置器,例如private string Whatever { get; set;}

  • 当您使用近域方法时,这非常方便
  • 设置值时,应检查特定的逻辑也很方便

这是何时使用私有设置器的完整说明:C#属性用法


如果需要,稍后将该属性公开也很容易。
汤姆·皮克斯

4
什么是近域方法
2014年

1
我指的是该类属性中的私有设置器。它应防止直接从对象访问设置值,并在设置值之前进行一些逻辑检查。
Yusubov

拥有带有公共setter / getter的私有变量(即使它们只是天真)也具有以下优点:如果将代码编译到库中,那么如果您将naive setter / getters替换为具有以下内容的代码,则签名将保持不变:效果等),但如果您改用简单的公共变量,则将库签名更改为带有非原始设置程序/获取程序的私有库签名也会更改。..这意味着如果您的库以这种方式更改,那么库的使用者将需要重新编译。
猎户座elenzil

12

这里已经有一些不错的答案,但是我认为其中大多数都错过了您的问题的要点:

我见过有人为每个成员(无论是否私有)都进行财产管理……这有意义吗?

我认为这对于每个成员来说都是很少必要的。从...开始

 private string Whatever;

以后当您需要对该特定成员进行封装或有条件断点时,您仍然可以将其替换为具有相同名称的属性-在大多数情况下,无需更改使用的代码Whatever。但是请注意,两者之间存在细微的差异,请参阅本SO帖子中的 Brian Rasmussen的答案 (链接也由Emmad Kareem提供,+ 1)。


+1,也是正确的,大多数答案都遗漏了最初的问题,我们是典型的IT专家:)
NoChance 2012年

事后将领域重构为财产是一场噩梦;要求您重新编译所有使用者。更改自动属性以包含自定义逻辑很容易。如果甚至很少有代码看到重构的机会,那么如果您只是从一开始就使用属性,那么它将减少很多工作。

@Dan:在大多数项目中,我看到的工作量是相同的,在两种情况下都必须重新编译。不过,我认为您是正确的,使用属性(尤其是自动属性)可以帮助避免反射或在“ out”参数中使用成员变量的某些细微问题。
布朗

@DocBrown无论如何,它绝对不是万能的声明。保持简单和使用字段有很多原因。如果您可以预测重构,那么即使是细微的问题也值得考虑。
2013年

7

这种方法的最大优点之一是,您可以控制何时更改变量

考虑以下。
您正在调试一个大型项目。某个方法会引发异常。您设置了一个断点,您发现某个成员变量具有错误的值。假设您的代码非常广泛地依赖于此变量,并且您发现数百种写用法(Spaghetti场景)。因此,您无法弄清楚其中哪些用法分配了错误的值。
如果代码是多线程的,调试可能会成为一场噩梦。

有了property,您可以在setter中设置一个断点。结合某个条件,可以在一次运行中将其确定下来。

VC ++具有数据断点,但仅适用于非托管代码。


1

如果不使用属性,则唯一的选择是使用字段存储变量数据。属性和字段具有显着差异。这些差异中的大多数都可以在字段与属性中找到。我建议您研究差异,然后根据您的应用程序需求进行选择。但是,请记住,由于字段的性质和缺点,使用字段代替属性不是常见的OO实践。


1

私有属性对于封装类内部事件的行为非常有用。仅仅因为它们是私有的,并不意味着您不应该利用属性赋予您的语法糖。


但是,给出的示例很糟糕。这样的样板属性获取器和设置器几乎总是一个坏主意。如果您使用的是C#3.0或更高版本,则自动属性是一个更好的主意:

private string Whatever { get; set; };

它更短,更干净并且可读性更高。实际上,它仅比声明后备变量长。

不过,最重要的是,可以将自动属性随时转换为完整属性,而无需更改程序其余部分的语义!因此,如果您需要添加验证或错误处理,则可以轻松实现。


就是这样,我一直以为您不能拥有只读属性,以仅能写入构造函数中的值(我更喜欢不可变类型),这是很可惜的,但这是在C#6.0中添加为“自动属性初始化程序”,您可以执行以下操作:

private string Whatever { get; } = ...;

要么

private string Whatever { get; };

随着

    Whatever = ...;

在构造函数中。


0

不一定非要对私有属性执行此操作,但是它确实允许修改属性获取/设置基础值的方式而无需更改对其的访问方式。

例如,修改基础值的设置方式:

private string Whatever
{
    get
    {
        return this.mWhatever;
    }
    set
    {
        this.mWhatever = string.IsNullOrEmpty(value) ? string.Empty : value;
    }
}

0

是啊

我个人将其用作缓存机制,我们可以称其为属性级缓存

private List<User> users;
private List<User> Users
{
    get
    {
        if(users == null) users = new UserManager().GetUsers();
        return users;
    }
}

现在,在班级内的其他地方,我使用Users属性而不是users字段。

另一种可行的情况可能是您想在某个字段上实现某种逻辑,但是要在类中以集中方式实现。因此,您都可以为字段创建GetFoo()方法或Foo属性foo

private string foo;
private string Foo
{
    get
    {
        return "Mr. " + foo;
    }
}

0

还有其他需要考虑的事项:

实际属性是实现细节。OOP的目标之一是在可行的情况下尽量减少对实施细节的暴露。

这样做的原因是,如果您隐藏属性并仅通过getter和setter公开它们,则您拥有更多的控制权。例如,您的getter可以验证其输入并防止将该属性设置为无效状态。如果更改值的能力是时间紧迫的,那么设置者也可以防止这种情况。无论出于何种原因,该获取器都应获取实际值,因此可以进行延迟初始化和/或缓存。

公开getter和setter并隐藏属性意味着有时您根本不需要属性。您的getter可以在调用时计算值,或将任务委派到其他地方,或使其符合要求的任何东西。关于您的班级,重要的是您可以从班级访问的信息,而不是内部信息的表示方式。

当然,如果类之外的任何事物都不能直接访问属性,则没有负面影响,那么您应该将其公开。但是,以我的经验来看,这种情况相对较少。


0

我知道这是一个古老的问题,@ Yusubov所说的一切都是正确的,但是关于第二个问题,即关于二传手中特定逻辑的要点,并且由于我没有看到任何人特别提及这一点,所以一个完美的例子就是当您上课时实现INotifyPropertyChanged接口。

WCFSilverlight中大量使用此功能,通过它的设置器中的逻辑,您可以知道何时更改了特定属性,如以下类中所示:

public class Settings : INotifyPropertyChanged
{
    public Settings() 
    { 
        this.CountryField = String.Empty; 
    }

    private string CountryField;
    public string Country
    {
        get { return this.CountryField; }
        set
        {
            if (Object.ReferenceEquals(this.CountryField, value)) { return; }

            this.CountryField = value;
            this.RaisePropertyChanged("Country");
        } 
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler propertyChanged = this.PropertyChanged;

        if (propertyChanged != null)
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}
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.