为啥要为属性创建私有字段?

Posted

技术标签:

【中文标题】为啥要为属性创建私有字段?【英文标题】:Why create private fields for properties?为什么要为属性创建私有字段? 【发布时间】:2016-06-04 19:45:06 【问题描述】:

我一直在研究与字段相比的属性示例,我经常看到的一件事没有解释是创建一个私有字段,然后是一个属性。为什么我们需要_foo,并且不能只运行类似于我下面的代码,或者将Foo = Foo 替换为Foo = value

我在运行它时看到它创建了一个堆栈溢出异常,所以我猜它正在寻找一个值来填充Foovalue,但我认为当我执行def.Foo = 55 时会处理它。

即使创建私有字段是最佳做法,我仍然希望更好地了解导致溢出的实际原因。

class ABC

    private int _foo; //Why create this?
    public int Foo
    
        get  return Foo; 
        set
        
            Foo = Foo;//more logic would go here. 
        
    


class Program

    static void Main(string[] args)
    
        ABC def = new ABC();
        def.Foo = 55;
        Console.WriteLine($"The value of Foo is def.Foo.");
        Console.ReadLine();
    

【问题讨论】:

你的 get 和 set 应该返回 _foo,而不是 Foo...并且设置 _foo 而不是 Foo。否则它是无限的,因此溢出 【参考方案1】:

那是因为fieldproperty不一样。

private int _foo; //field
public int Foo //property

    get  return _foo;  //should be _foo
    set
    
        _foo = value;
    

通常,您使用 public property 来访问 private field,因为您可能想要做一些其他事情而不是分配/获取值领域。

例子:

private bool selectedIndexChanged; //note this additional field
public bool SelectedIndexChanged 
    get  return selectedIndexChanged; 
    set  selectedIndexChanged = value; 


private int selectedIndex; //Why create this?
public int SelectedIndex

    get  return selectedIndex; 
    set
    
        selectedIndexChanged = value != selectedIndex; //you update selectedIndexChanged here!
        selectedIndex = value;
    

请注意,您不能直接访问私有字段,而是通过公共属性访问它们。拥有属性的好处是,您不仅可以读取/分配字段值,还可以执行其他操作:检查输入的有效性、分配其他字段、调用事件等等……

即使创建私有字段是最佳做法,我仍然会 想要更好地了解实际上是什么导致了 溢出。

溢出不是最佳实践引起的,而是由错误引起的> 练习:

private int _foo; //Why create this?
public int Foo

    get  return Foo;  //this should not happen, wrong practice
    set
    
        Foo = Foo; //neither should this happen, another wrong practice
    

上面代码中发生的事情是:

    你得到 Foo,它返回 Foo 你需要得到 Foo,你需要得到 Foo,它返回 Foo 你需要得到 Foo 返回 Foo,你需要得到 Foo,它返回 Foo 你需要get Foo 返回 Foo,你需要获取 Foo,返回 Foo,你需要获取 Foo 返回 Foo,你需要获取 Foo,返回 Foo,你需要获取 Foo 返回 Foo,你需要获取 Foo, which return Foo 你需要获取 Foo 返回 Foo,你需要获取 Foo,which 返回 Foo 你需要获取 Foo 返回 Foo,你需要获取 Foo,它返回 Foo 你需要获取 Foo 返回 Foo,你需要得到Foo,返回Foo,你需要得到Foo,返回Foo,你需要得到Foo,返回Foo,你需要得到Foo,返回Foo,你需要得到Foo,返回Foo,你需要得到 Foo 返回 Foo,你需要得到 Foo...直到无穷大

同样:

    你设置了Foo,需要获取Foo,返回Foo需要获取Foo,需要获取Foo,返回Foo,需要获取Foo返回Foo,需要获取Foo,返回Foo你需要得到Foo返回Foo,你需要得到Foo,返回Foo你需要得到Foo返回Foo,你需要得到Foo,返回Foo你需要得到Foo返回Foo,你需要获取 Foo,返回 Foo 你需要获取 Foo 返回 Foo,你需要获取 Foo,返回 Foo 你需要获取 Foo 返回 Foo,你需要获取 Foo,返回 Foo 你需要获取Foo 返回 Foo,你需要获取 Foo,返回 Foo,你需要获取 Foo 返回 Foo,你需要获取 Foo,返回 Foo,你需要获取 Foo 返回 Foo,你需要获取 Foo,它return Foo,你需要得到 Foo 返回 Foo,你需要得到 Foo...直到无穷大

从而导致溢出...

【讨论】:

【参考方案2】:

为什么我们需要 _foo,而不能只运行类似于我的代码 有以下,或者替换?

因为属性是设置或获取字段值的方法。所以,你必须先定义一个字段。如果您必须在内部暗示任何逻辑,尤其是在 setter 中,则这是必需的。否则,更常见的是使用自动实现的属性。

即使在我们使用自动实现的属性的情况下,编译器也会为我们生成一个支持字段。

public Person

    public string FirstName  get; set; 

例如,这样做

var person = new Person  FirstName = "Bob" ;

您创建一个新的Person 对象,其属性名为FirstName,其类型为字符串。此外,您将人员的FirstName 的值设置为 Bob。这是您必须注意的一点。编译器会生成一个字符串类型的支持字段,并且在运行时这将是存储对人名的引用的地方。

上述类的定义与以下类似:

public Person

    private string _firstName;
    public string FirstName
    
        get  return _firstName; 
        set  _firstName = value; 
    

实际上,当您定义自动实现的属性时,编译器在幕后会做什么。基于此,我们可以说对字符串“Bob”的引用将存储到_firstName

【讨论】:

除了属性根本不需要访问任何字段。考虑DateTime.Now【参考方案3】:

属性实际上是实际变量的获取/设置方法。但是您并不总是需要拥有私有变量才能拥有属性。

你可以简单地拥有

public int Foo  get; set; 

编译器会神奇地为您创建和设置变量。 但是,如果您需要限制属性被分配错误的值或执行一些额外的任务,则需要属性变量。

例子:

private int _score;
public int Score

    get  return _score; 
    set
    
        if(value > 100)
            throw new Exception("Score cannot be more than 100");

        _score = value;
    

【讨论】:

以上是关于为啥要为属性创建私有字段?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能为 CDTVC 中的私有 UIRefreshControl 属性创建 Getter?

为啥私有字段是类型私有的,而不是实例私有的?

我应该使用公共属性和私有字段还是公共字段来存储数据?

为啥 Quarkus 会警告我关于私有字段的注入?

为啥可以毫无问题地测试 Spock 中的私有方法/字段?

具有公共getter的抽象属性,可以在具体类中定义私有setter吗?