关于c#:为什么不直接使用类字段?

Why not use class fields directly?

本问题已经有最佳答案,请猛点这里访问。

所以我知道类和结构是数据的结构。类字段默认为私有字段,结构字段为公共字段。对于C++和EDCOX1,类似EDCOX1,0,C=1。

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

但是为什么呢?很多人告诉我"其他人可以通过这种方式访问你的变量,所以让它们成为私有的"。我不明白,这有什么区别使他们公开,只使用他们的obj.a = 3;或使他们私人,做obj.SetValue(3);?有人能(甚至是简短地)解释这些区别是什么,以及当这些领域公开时,S.O.如何才能进入这些领域?


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

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

而且,使用属性并没有太多的缺点。所以利大于弊。


不同语言的答案不同。

在C++中,将属性私有化并提供GETER和SETTER方法是一个好主意,因为C++提供了常量正确性,并且不能在const对象上调用SET。

在不提供const正确性的c中,除了简单地将所有属性作为公共属性提供外,似乎没有任何意义,因为只要有一个setter,就可以随时调用它。

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

1
2
3
4
5
6
class Contrived
{
    private List<Things> m_List = new List<Things>();

    public List<Things> LIST{ get {return m_List;} }
};

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

1
2
3
4
5
6
class Contrived
{
    private List<Things> m_List = new List<Things>();

    public IEnumerable<Things> LIST{ get {return m_List;} }
};

这样,我们就可以通过使用接口来伪造const的正确性,尽管在我看来,它不如拥有一种const正确的语言好。;-)实际上,在上一个示例中,我们提供了一个不能修改但内容可以是谁的列表,因此我们可以实现这样的属性(假设事物实现了IUnmodableThing):

1
2
3
4
5
6
7
8
9
10
11
12
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(取决于语言)可以做的不仅仅是赋值或返回,那么它们可能非常强大。


那么,如果您以后想将该属性更改为数据库调用呢?这对于一个领域来说是不可能的。另外,如果您不希望从类外部而不是在类内部设置属性,该怎么办?如果您希望更改它,以便只设置非空值,该怎么办?当值更改时,您能收到通知吗?你不能用一个场和所有的getters+setter来做这些。


这种行为被广泛认为是"良好的编程方式",以便始终向类用户提供一个界面,他可以自由使用,而不必担心破坏其中的任何内容。这就是为什么要关闭类属性在类访问器方法中的更改。当改变一个字段也改变了其他一些字段并对类产生影响时,它非常有用。此外,当您试图找到某一时刻某人正在更改某个破坏某个内容的字段值时,它非常有用;)


范围界定——例如,公共的和私有的是一种告诉人们如何使用你的类的方法。

任何公共的(或C++中保护的)都是你与你的类用户的合同的一部分。

这意味着:

你不应该公开改变任何事情

您可以自由地更改任何私有的内容,因为这些内容被视为类的实现细节。


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

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

1
2
3
4
5
6
7
8
9
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之类的东西的写访问权是不明智的。


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