为什么不直接使用类字段呢

本文关键字:字段 为什么不 | 更新日期: 2024-04-23 16:28:19

所以我知道类和结构是数据的结构。类字段默认为私有字段,结构字段为公共字段。类似于C++的public: / int a;和C#的public int a;

但是访问这些字段的其他方法是使它们私有并使用函数/方法。就像SetValue(int value){a = value;}GetValue() { return a; },或者我甚至听说过C#中新的酷炫{set; get;}

但是为什么许多人告诉我"其他人可以通过这种方式访问您的变量,所以请将其设为私有"。我不明白,把它们公开并只使用obj.a = 3;,或者把它们私有并使用obj.SetValue(3);,有什么区别?有人(甚至简短地)能解释一下有什么区别吗?当这些字段公开时,s.o.如何访问这些字段?

为什么不直接使用类字段呢

使用属性(getter和setter)而不是公共字段(公开实例变量)的原因有很多。其中一些是:

  • 您可以允许对外部类进行只读访问(通过提供getter,但不提供setter)
  • 您可以在访问该值时计算该值
  • 您可以在不破坏现有代码的情况下更改访问/计算值的方式
  • 您可以在不破坏现有代码的情况下更改内部表达
  • 您可以覆盖子类中的属性,这在字段中是不可能的
  • 等等

此外,使用属性并没有太多负面影响。所以利大于弊。

不同语言的答案不同。

在C++中,最好将属性设为私有,并提供getter和setter方法,因为C++提供常量正确性,并且不能在常量对象上调用setter。

在不提供常量正确性的C#中,除了简单地将所有属性提供为公共属性之外,做其他事情似乎毫无意义,因为一旦有了setter,就可以随时调用它。

但是,例如,如果属性是一个容器(例如List),该怎么办。那么,你可能只希望人们能够操作列表,而不希望将列表指针设置为新值,所以你可以这样定义属性:

class Contrived
{
    private List<Things> m_List = new List<Things>();
    public List<Things> LIST{ get {return m_List;} }
};

或者,你可能只希望人们能够检查列表,而不希望添加或删除列表中的内容:

class Contrived
{
    private List<Things> m_List = new List<Things>();
    public IEnumerable<Things> LIST{ get {return m_List;} }
};

通过这种方式,我们正朝着通过使用接口来伪造常量正确性的方向迈进,尽管IMO不如仅仅拥有一种常量正确的语言。;-)事实上,在上一个例子中,我们提供了一个不能修改的列表,但谁的contents可以修改,所以我们可以实现这样的属性(假设Things实现IUnmoddableThing):

class Contrived
{
    private List<Things> m_List = new List<Things>();
    public IEnumerable<IUnmoddableThing> LIST{ 
    get 
    {
        List<IUnmoddableThing> temp = new List<IUnmoddableThing>();
        ... copy m_List into temp ...
        return temp;
    } }
};

显然,这使我们能够比简单地公开成员属性拥有更多的控制权。那是在我们进入一处房产之前,我们实际上,也许。。。哦,通过定制的军用无线电携带的插座连接到远程数据库。。。

一旦您意识到setter在编译时可以被拒绝使用(在常量正确的语言中),getter/setter或get/set(取决于语言)可以做的不仅仅是简单的赋值或返回,它们就会非常强大。

范围界定-例如,这是一种告诉人们如何使用您的类的方法

任何公开的(或在C++中受保护的)内容都是您与类用户签订的合同的一部分

这意味着:

你不应该更改任何公共

您可以自由更改任何私有的东西,因为它被认为是类的实现细节

这种行为被广泛认为是"良好的编程方式",以便始终为类用户提供一个他可以自由使用的界面,而不必担心破坏其中的任何东西。这主要是为什么应该关闭类似访问器的方法中不断更改的类属性。当更改一个字段也会更改其他字段并对类产生影响时,它非常有用。此外,当你试图发现有人更改字段值时,它会派上用场;)

如果您以后想将该属性更改为数据库调用,该怎么办?这在有田地的情况下是不可能的。此外,如果您不希望属性可以从类外部设置,但不能在类内部设置,该怎么办?如果你想更改它,使你只能设置非null值,该怎么办?当值更改时,你能收到通知吗?你不能用一个场来做任何这些,也不能用getter+setter来做所有这些。

这是调用封装,允许您正确管理对类信息的访问:封装

封装并不总是需要的,但强烈建议隐藏类内部并防止未处理的数据管理,例如,您可以向setter添加一些检查,如果数据是公共的,则无法阻止:

int SomeClass::setPointer(char *point)
{
   if(!point)
   {
      cout << "Trying to assign null pointer" << endl; //Or, better, throw exception
      return error; // Or, better, throw exception
   }
   mPointer = point;
}

这是最基本的优势,但应该还有其他类似的:

  • 您可以在每次修改主数据时自动更新相关的控制数据(例如,每次添加新元素时的数组大小)
  • 样式:它更容易维护(例如,您更改成员的类型,您只需要在setter中添加一个强制转换,而不需要修改调用该字段的所有行)
  • 提高可用性
  • 在某些情况下,性能会更好
  • 等等

其他答案列出了许多封装的充分理由。我只想补充一个最基本的原因:

  • 保护类的不变量

如果你的类被设计为保持某种不变,你就不能给类的用户任何破坏它的方法。例如,如果你设计了一个容器,那么给用户写访问capacitysize之类的东西是不明智的。

此外,类中数据成员的值很少是独立的,直接访问(更改值)数据很容易使对象处于不一致的状态。