为什么要为属性创建私有字段?

Why create private fields for properties?

我一直在查看属性与字段的示例,我总是看到没有解释的一件事是创建私有字段,然后是 属性。为什么我们需要 _foo,而不能只是 运行 类似于我下面的代码,或者用 Foo = value 替换 Foo = Foo

我看到它在我 运行 它时创建了一个堆栈溢出异常,所以我猜它正在寻找一个值来填充 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();
    }
}

那是因为字段属性不一样。

private int _foo; //field
public int Foo //property
{
    get { return _foo; } //should be _foo
    set
    {
        _foo = value;
    }
}

通常,您使用 public 属性 访问 私有字段 因为您 可能 想要做一些不同于 assigning/getting 字段值的事情。

示例:

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;
    }
}

请注意,您不能直接访问私有字段,而是通过 public 属性访问它们。拥有属性的好处在于,您不仅可以 read/assign 字段值,还可以 做其他事情 :检查输入的有效性,分配一些其他字段、调用事件等...

Even if it is best practice to create the private field, I'd still like to get a better understanding of what's actually causing the overflow.

溢出 不是 是由 best 实践引起的,而是由 错误做法:

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
    }
}

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

  1. 你得到 Foo,return Foo 你需要得到 Foo,你需要得到 Foo,return Foo 你需要得到 Foo return Foo,你需要得到 Foo,return Foo 你需要得到 Foo return Foo,你需要得到 Foo,return Foo 你需要得到 Foo return Foo,你需要得到 Foo,return Foo 你需要得到 Foo return Foo,你需要得到 Foo,return Foo 你需要得到Foo return Foo,你需要得到 Foo,return Foo 你需要得到 Foo return Foo,你需要得到 Foo,return Foo 哪个你需要得到 Foo return Foo,你需要得到 Foo,它 return Foo 你需要得到 Foo return Foo,你需要得到 Foo,它 return Foo 需要得到 Foo return Foo 需要得到 Foo 需要得到 Foo return Foo 需要得到 Foo return Foo 需要得到 Foo。 .. 直到无穷大

同样:

  1. 你设置了Foo,需要获取哪个Foo return 你需要获取哪个Foo Foo,你需要获取哪个Foo,哪个return 你需要获取哪个Foo Foo return Foo,你需要得到 Foo,return Foo 你需要得到 Foo return Foo,你需要得到 Foo,return Foo 你需要得到Foo return Foo,你需要得到 Foo,return Foo 你需要得到 Foo return Foo,你需要得到 Foo,return Foo 哪个你需要得到 Foo return Foo,你需要得到 Foo,它 return Foo 你需要得到 Foo return Foo,你需要得到 Foo,它 return Foo 你需要得到 Foo return Foo 你需要得到 Foo 哪个 return Foo 你需要得到 Foo return Foo 你需要得到 Foo, which return Foo 你需要获取 Foo return Foo 你需要获取 Foo which return Foo 你需要获取 Foo return Foo 你需要得到 Foo... 直到无穷大

从而造成溢出...

Why do we need _foo, and can't just run the code similar to what I have below, or maybe replace ?

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

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

public Person
{
    public string FirstName { get; set; }
}

例如,这样做

var person = new Person { FirstName = "Bob" };

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

上面class的定义和下面的类似:

public Person
{
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set { _firstName = value; }
    }
}

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

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

你可以简单地

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;
    }
}