OO设计 – 您是否在内部使用公共属性或私有字段?

我在C#2.0工作,但这适用于大多数面向对象的语言。 当我创建具有包装私有字段的公共属性的类时,我会在内部是否应该使用属性或字段之间来回切换。 当然C#3.0使自动属性更容易,但它仍然可以应用。

有关系吗?

public class Person { private string _name = ""; public string Name { get { return _name; } set { _name = value; } } public Person(string name) { _name = name; //should I use the property or field here? } } 

基本上,因为您可以在属性中实现validation和其他逻辑,所以除非有特殊原因,否则应该通过属性进行访问。

它有助于保持对象的一致性,因为这样您就知道私有字段的值已经经过了您选择放入访问器或setter方法的任何严格要求。

另一方面,构造函数可能是一个例外,因为您可能想要设置初始值。

但总的来说,我会说要通过该物业进入。

编辑

一个(琐碎的/做作的)例子

 public class Person { private string _name = ""; private List oldnames = new ArrayList(); public string Name { get { return _name; } set { oldnames.Add(_name); _name = value; } } public Person(string name) { _name = name; //should I use the property or field here? } } 

因此,在这种情况下,您希望构造函数跳过该属性,但如果您再次使用该字段,则会导致代码中出现错误,因为您正在跳过“名称归档”。 将validation放在您的属性中的原因是您不需要在访问该字段的每个位置复制validation代码,因此即使在私有方法中也不应跳过它。

我建议使用该物业,你永远不知道你什么时候在你的财产内做某事,即。 延迟加载,转换,格式化,计算等,如果您使用私有成员将引入错误…

大多数情况下它类似于代码首选项,但我更喜欢使用公共属性或私有属性,您可以简单地向属性添加一些逻辑,而无需对调用者代码进行任何更改。 同样在3.5 .NET中,您可以使用有用的代码糖作为autoproperty。

如果使用字段名称,则不会获得封装。 封装不仅适用于类,也适用于类。

如果在某个未来的某个时刻,你重新定义了字段,但是将访问者/ setter函数的签名保持不变,不仅使用你的类的外部类不会中断,而且你自己的类中的内部例程也不会中断。

以这个代码为例,松散地基于Apache Wicket框架中显示的代码(尽管框架没有遇到这个问题;我只是用它来说明):

让我们说这是原来的类:

 class RadioChoice { private String prefix; RadioChoice( String prefix ) { this.prefix = prefix ; setPrefix( String prefix ) { this.prefix = prefix ; } } 

在这里我们已经可以看到一个问题:相同的操作this.prefix = prefix发生在两个地方。 这意味着做同样的事情,但由于它发生在两个地方,“同样的事情”可以分为两个不同的东西。 (将此与数据库规范化进行比较,这是一种旨在防止这种情况的实践。)

现在,Apache Wicket有一个概念,即记录如何呈现网页的更改,以便可以撤消这些更改。 它通过存储“撤消”对象列表来实现。 RadioChoice的前缀是可以撤消的东西之一,因此’prefix’设置器必须记录更改:

 class RadioChoice { private String prefix; RadioChoice( String prefix ) { this.prefix = prefix ; setPrefix( String prefix ) { // save the old prefix this.getPage().addChange( new PrefixChange( this.prefix ) ); // set the new one this.prefix = prefix ; } } 

现在看看会发生什么:因为构造函数直接设置了前缀,所以ctor不会保存任何更改。 如果我们使用了setter,ctor会在重构后自动“做正确的事情”。 相反,我们必须重构setter ctor。

当然,我们的代码仍然不是最优的,实际上它应该是这样的:

  setPrefix( String prefix ) { // save the old prefix this.getPage().addChange( new PrefixChange( this.getPrefix() ) ); // set the new one this.prefix = prefix ; } } 

Scott Meyers有一系列关于此的文章,他主张(在C ++中,它具有自由函数)一个类公开原始函数( 原始函数:除了类iteslf之外不可能的函数)以及所有可以组成的函数从原始函数中获取自由函数。 这使得界面更精简,界面更稳定。

同样,如果您可以将类中的函数视为仅依赖于其原始接口,则您具有更大的灵活性; 特别是,这些职能是候选人被移出课堂。 更一般地说,使用setter和getter可以使您免受更改。

使用该属性。 或者更好的是,如果您使用C#3.0或更高版本,请使用自动属性,如下所示:

 public class Person { public string Name { get; set; } public Person(string name) { Name = name; //should I use the property or field here? } } 

如果您现在使用该属性,则在修改代码以使用“自动属性”时您将做好准备。

可能存在您必须在内部访问该字段的情况。 例如,当您仅公开提供对属性的只读访问权限时,在实例化对象后不允许更改该值。

 public class MyClass { private string _MyProperty; public MyProperty { get { return _MyProperty; } } public MyClass () { _MyProperty = SomeVeryComplexPropertyInitializationLogic (); } } 

通常我使用公共属性有一些例外,我可能想避免在属性访问器中写入任何额外的代码。 使用C#3.0中的自动属性,我很少手工创建支持字段; 通常仅在自动备份字段的默认值不适合我的属性时。

至少在Java中,没有办法使用公共字段,因为每个人似乎都使用了很好的旧javabeans约定。 我猜C#具有属性作为第一语言结构。 我会去那

你应该使用“Name”(属性),因为如果你想对这个值进行一些检查,你可以将它放入“Name”属性的setter中,然后将用于构造函数并最近设置值。

这取决于。 有时,您会编写getter / setter,对与该字段的任何实际交互进行重要的预处理。 例如,如果字符串字段的约束必须始终为小写,那么至少有一个getter / setter方法必须调用.ToLower()或.ToLowerInvariant(),并且在类代码中,您可能想要使用它来确保强制执行约束。

但是,有时您需要绕过预处理逻辑。 事实上,我已经看到开发人员无意中通过使用公共属性而不是私有字段创建无限循环的时代(不能想到一个例子,对不起)。

Linq To SQL生成的类是一个很好的例子,我认为,因为它们显示了一个Property中可以存在多少逻辑。 尝试编写一些扩展方法,您将开始了解其中的差异。

我认为最重要的是它取决于你在类中任何给定点使用什么样的逻辑,以及getter / setter中存在什么样的预处理。 如果您不确定或似乎无关紧要,最好使用getters / setters / public属性,以便编写更易于维护的代码,这些代码将遵循稍后添加的约束。