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

时间:2009-04-09 15:35:43

标签: c# oop coding-style

我正在使用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?
    }
}

10 个答案:

答案 0 :(得分:14)

基本上,因为你可以在属性中实现验证和其他逻辑,所以你应该通过属性访问,除非你有特殊的理由不这样做。

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

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

但总的来说,我会说通过财产进入。

修改

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

public class Person
{
    private string _name = "";
    private List<String> 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?
    }
}

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

答案 1 :(得分:2)

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

答案 2 :(得分:2)

我建议使用该属性,你永远不知道你什么时候在你的财产内做某事,即。延迟加载,转换,格式化,计算等,如果你使用私人成员会引入一个错误...

答案 3 :(得分:2)

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

如果在某个未来的某个时刻,你重新定义了字段,但保持accessor / 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可以使您免受更改。

答案 4 :(得分:1)

使用该属性。或者更好的是,如果您使用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?
    }
}

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

答案 5 :(得分:1)

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

public class MyClass
{
    private string _MyProperty;

    public MyProperty { get { return _MyProperty; } }

    public MyClass ()
    {
        _MyProperty = SomeVeryComplexPropertyInitializationLogic ();
    }
}

答案 6 :(得分:0)

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

答案 7 :(得分:0)

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

答案 8 :(得分:0)

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

答案 9 :(得分:0)

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

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

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

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