C#中的{ get; set; }语法是什么?

733

我正在学习ASP.NET MVC,我可以阅读英文文档,但是我真的不太理解这段代码在做什么:

public class Genre
{
    public string Name { get; set; }
}

{ get; set; }是什么意思?


12
一般来说,记住——设置器会使你的对象可变,这是一个不好的想法。获取器违反了"命令一个对象做事,不要询问它的信息并自行操作"的原则。因此,一般情况下,不要默认添加设置器和获取器。你通常需要它们,但在添加它们之前,应始终找到真正的需求。特别是在生产代码中几乎不应使用设置器(尽可能实现不可变性,在需要改变时,应请求其进行更改,而不是设置一个值)。 - Bill K
16
补充一点内容... 如果你不加 {get; set;},你创建的是一个字段;但如果你加上{get; set;},你创建的是一个 属性(Property)。使用属性可能会使一些事情变得更容易,特别是在使用反射时。 - Seichi
2
@Seichi 使用 get-setter 创建了一个字段,但这个字段是隐藏的,声明为私有的,并由自动生成的属性进行修改;所有这些都是由编译器完成的。 - Jonathan Ramos
2
自动属性不是违背了“私有”字段的目的吗? - mireazma
21个回答

697

这是所谓的自动属性,本质上是以下代码的简写形式(编译器将生成类似的代码):

private string name;
public string Name
{
    get
    {
        return this.name;
    }
    set
    {
        this.name = value;
    }
}

126
Klaus,你能解释一下这段代码会发生什么吗?它可能需要更详细的说明。 - TylerH
3
所以,只是为了确认一下:这就像我重载了 = 运算符,但只针对一个特定的元素,是这样吗? - Hi-Angel
10
为什么我们需要私有变量? :-/ 羞耻。 - Oliver Dixon
4
@mpag & dtgq,“value”是一个隐式参数,参见MS使用属性。这是一个简单而不是糟糕的设计选择。类型检查被保留。 - stefano
8
可能很明显,但我想澄清一下,这个简写并不是字面意义上的私有变量"name"的简写。也就是说,在类内部如果你试图引用这个私有变量,它会失败。我不确定C#是如何实现的,但它的行为就好像有一个没有名称的私有变量,在代码中无法访问它。 - Denziloe
显示剩余7条评论

535

据我理解,{ get; set; } 是一种“自动属性”,就像@Klaus和@Brandon所说的那样,它是写一个带有“支持字段”的属性的简写。因此,在这种情况下:

public class Genre
{
    private string name; // This is the backing field
    public string Name   // This is your property
    {
        get => name;
        set => name = value;
    }
}

然而,如果你和我一样——大约一个小时前——你可能并不真正理解属性和访问器是什么,也没有对一些基本术语有最好的理解。MSDN是学习这类知识的好工具,但对于初学者来说并不总是易于理解。所以我在这里尝试更深入地解释一下。
get和set是访问器,意味着它们能够访问私有字段(通常是从后备字段)中的数据和信息,并且通常从公共属性(如上面的示例中所示)中进行访问。
毫无疑问,以上声明相当令人困惑,因此让我们进入一些示例。假设这段代码引用的是音乐类型。因此,在Genre类中,我们将希望有不同的音乐类型。假设我们想要有三种类型:嘻哈、摇滚和乡村。为此,我们将使用该Class的名称来创建该类的新实例。
Genre g1 = new Genre(); //Here we're creating a new instance of the class "Genre"
                        //called g1. We'll create as many as we need (3)
Genre g2 = new Genre();
Genre g3 = new Genre();

//Note the () following new Genre. I believe that's essential since we're creating a
//new instance of a class (Like I said, I'm a beginner so I can't tell you exactly why
//it's there but I do know it's essential)

现在我们已经创建了 Genre 类的实例,可以使用之前设置的“Name”属性来设置流派名称。

public string Name //Again, this is the 'Name' property
{ get; set; } //And this is the shorthand version the process we're doing right now 

我们可以通过以下方式将'g1'的名称设置为Hip Hop:
g1.Name = "Hip Hop";

这里发生的事有点复杂。如我之前所述,getset可以访问私有字段,否则您将无法访问。 get只能读取该私有字段中的信息并返回它。 set只能在该私有字段中写入信息。但通过具有getset的属性,我们能够执行这两个功能。通过编写g1.Name = "Hip Hop";,我们特别使用了我们的Name属性的set函数。 set使用一个隐含变量称为value。基本上,这意味着任何时候在set中看到"value",它都是指变量;"value"变量。当我们编写g1.Name = 时,我们使用=来传递value变量,在这种情况下是"Hip Hop"。因此,您可以将其视为:
public class g1 //We've created an instance of the Genre Class called "g1"
{
    private string name;
    public string Name
    {
        get => name;
        set => name = "Hip Hop"; //instead of 'value', "Hip Hop" is written because 
                              //'value' in 'g1' was set to "Hip Hop" by previously
                              //writing 'g1.Name = "Hip Hop"'
    }
}

需要注意的是上面的例子实际上并没有写在代码中,它更像是一个假想的代码,代表了后台正在发生的事情。

现在我们已经设置Genre的g1实例的名称,我相信我们可以通过编写代码获取该名称。

console.WriteLine (g1.Name); //This uses the 'get' function from our 'Name' Property 
                             //and returns the field 'name' which we just set to
                             //"Hip Hop"

如果我们运行这个程序,我们将在控制台中得到"Hip Hop"

因此,为了说明问题,我将在示例中提供输出结果。

using System;
public class Genre
{
    public string Name { get; set; }
}

public class MainClass
{
    public static void Main()
    {
        Genre g1 = new Genre();
        Genre g2 = new Genre();
        Genre g3 = new Genre();

        g1.Name = "Hip Hop";
        g2.Name = "Rock";
        g3.Name = "Country";

        Console.WriteLine ("Genres: {0}, {1}, {2}", g1.Name, g2.Name, g3.Name);
    }
}

输出:

"Genres: Hip Hop, Rock, Country"

22
我会将其注释掉,如下所示:set{name = value;} // 这里的'value'等于"Hip Hop" - maraaaaaaaa
2
@iLoveUnicorns,它的存在是为了数据抽象。支持字段包含实际数据。属性定义实际上定义了如何使用getset方法访问数据。我提供的链接中有John Guttag在页面顶部的优秀引用。我建议阅读他的书籍或甚至参加这个免费在线课程 - Josie Thompson
8
我们能不能只使用:`public class Genre{public string Name;}`而不是:`public class Genre{ public string Name { get; set; }}`。我的意思是,我们为什么需要 { get; set; }? - user2048204
3
似乎我的担忧已经被回应了。如果你这样声明:"public string Name { get; set; }",并且这样访问:g1.Name = "Hip Hop"; - 那么面向对象在哪里?我甚至不需要所谓的“后备字段”。就我而言,后备字段根本不存在。因为我只访问公共字段。如果公共字段是“公共”的,那么它就不符合OO标准。让我们都回到COBOL吧。 - Baruch Atta
4
好的,如果我们要苛求一点,"set"是一个修改器而不是一个访问器。 - pythlang
显示剩余12条评论

111

这些是自动属性

基本上是使用支持字段写属性的另一种方式。

public class Genre
{
    private string _name;

    public string Name 
    { 
      get => _name;
      set => _name = value;
    }
}

10
“Backing field” 是什么意思?“Backing field” 指的是一个类中用于存储属性值的隐藏字段。 - tree em
5
后台字段是存储数据的地方,它是使用get时返回和使用set持久化的地方。就像是一个橱柜,getset打开它的门来访问。 - Grant Thomas
6
在这个回答中,私有字段是_name。在自动属性中,私有字段是隐藏的。 - Justin

38

这是简便的做法:

public class Genre
{
    private string _name;

    public string Name
    {
      get => _name;
      set => _name = value;
    }
}

38

这是一种将数据成员公开的快捷方式,所以您不需要显式地创建私有数据成员。C#会为您创建一个私有数据成员。

如果您决定更改数据成员的实现方式以添加一些逻辑,则可以仅将数据成员公开而不使用此快捷方式,但那样的话您就需要破坏接口。因此,简而言之,它是创建更灵活代码的快捷方式。


2
Kelsey - 你能解释一下这个语法如何使代码更加“灵活”吗?我看不出来。如果你要为setter或getter添加任何“逻辑”,那么在有或没有私有数据的情况下,你仍然会破坏接口,并需要进行一些编码。 - Baruch Atta
@BaruchAtta:将自动属性更改为非自动属性或反之不会破坏接口。接口表示将有一个getter或setter属性,而不是如何实现它。事实上,在不查看代码的情况下,唯一区分它们的方法是查看生成的IL并查看其中一个具有奇怪的名称而另一个没有(在其他CLI语言中甚至可能不是这样,并且它不是C#规范的一部分,因此未来或分支版本不必这样做)。 - jmoreno

32

基本上,它是以下内容的快捷方式:

class Genre{
    private string genre;
    public string getGenre() {
        return this.genre;
    }
    public void setGenre(string theGenre) {
        this.genre = theGenre;
    }
}
//In Main method
genre g1 = new Genre();
g1.setGenre("Female");
g1.getGenre(); //Female

5
这并没有回答问题。OP在谈论属性。 - theB
8
我了解"Get"和"Set"的属性,这是一个例子,有助于更好地理解。 - Jirson Tavera
3
@theB 实际上,OP 是在询问 { get; set; } 的含义,所以我认为这个答案对于那些来自其他编程语言的人来说是一个好答案。 - carloswm85

15

基本上,它有助于保护您的数据。考虑以下没有setter和getter的示例以及具有它们的相同示例。

没有setter和getter

学生类

using System;
using System.Collections.Generic;
using System.Text;

namespace MyFirstProject
{
    class Student
    {
        public string name;
        public string gender;
        public Student(string cName, string cGender)
        {
            name = cName;
            gender= cGender;
        }

     }
}

在主函数中

 Student s = new Student("Some name", "Superman"); //Gender is superman, It works but it is meaningless
 Console.WriteLine(s.Gender);

使用setter和getter方法

using System;
using System.Collections.Generic;
using System.Text;

namespace MyFirstProject
{
    class Student
    {
        public string name;
        private string gender;
        public Student(string cName, string cGender)
        {
            name = cName;
            Gender = cGender;
        }

        public string Gender
        {
            get { return gender; }
            set
            {
                if (value == "Male" || value == "Female" || value == "Other")
                {
                    gender = value;
                }
                else
                {
                    throw new ArgumentException("Invalid value supplied");
                }
            }
        }
    }
}

在主函数中:

  Student s = new Student("somename", "Other"); // Here you can set only those three values otherwise it throws ArgumentException.
Console.WriteLine(s.Gender);

3
我刚开始学习C#,但我认为这是一篇好的解释。 - carloswm85
你的例子中,“value”是什么意思?谢谢。 - Dory Nguyen
@DoryNguyen:看起来"value"是set函数的隐式参数。因此,如果我调用myObject.Property = 875,set函数将把875分配给变量"value"。你只需要知道这是语法。同样,"get"希望你返回适当类型的值。 - MichaelS
1
很好的解释,但是Name { get; set; }是什么意思? - john k

10

1
额...这是否意味着您保留了对字符串的nil引用,然后在调用get; set;时从标准位置加载其值? - Aurum Aquila
1
是的,它会像任何“字符串”变量一样保留“null”,直到“someInstanceOfGenere.Name =“someValue””。 - Daniel A. White

9
  • get/set模式提供了一种结构,允许在实例化的类属性实例的设置('set')或检索('get')期间添加逻辑。当属性需要一些实例化逻辑时,这将非常有用。

  • 属性可能仅具有“get”访问器,这是为了使该属性只读

  • 在实现get/set模式时,中间变量用作容器,其中可以放置一个值并提取一个值。通常使用下划线作为中间变量的前缀。 这个中间变量是私有的,以确保它只能通过它的get/set调用进行访问。请参见Brandon的答案,因为他的答案演示了实现get/set的最常用语法约定。


6

它们是公共属性Name的访问器。

您可以使用它们来获取/设置Genre实例中该属性的值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接