一、提要
属性是一种特殊的类成员。我们使用预定义的set和get方法来访问和修改它们。属性读取和写入被转换为获取和设置方法调用。
二、何为属性
使用字段表示法(例如object.Name)访问变量比使用自定义方法调用(例如object.GetName)更容易。然而,利用属性,我们仍然具有封装和信息隐藏的优势。换句话说,属性将数据与外部世界隔离,同时具有方便的字段访问。
接口可以有属性,但不能有字段。
属性可以是读写(它们同时具有get和set访问器)、只读(它们只有get访问器)或只写(它们只有set访问者)。
Program.cs
var p = new Person();
p.Name = "Jane";
Console.WriteLine(p.Name);
class Person
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
我们有一个具有一个属性的简单Person类。
var p = new Person(); p.Name = "Jane"; Console.WriteLine(p.Name);
我们创建Person类的一个实例。我们使用字段符号访问成员字段。
public string Name { ... }
我们有一个名为Name的属性。它看起来像一个常规方法声明。不同之处在于它具有称为get和set的特定访问器。
get { return _name; } set { _name = value; }
get属性访问器用于返回属性值,set访问器用于分配新值。value关键字用于定义集合索引器分配的值。
$ dotnet run
Janes
三、只读属性 read-only properties
可以创建只读属性。为了创建只读属性,我们省略了set访问器,并在实现中仅提供get访问器。
Program.cs
var p = new Person();
// p.Name = "Beky";
Console.WriteLine(p.Name);
class Person
{
private string _name = "Jane";
public string Name
{
get { return _name; }
}
}
在示例中,我们演示了只读属性的使用。
// p.Name = "Beky";
该行现在已注释。我们不能更改属性。如果我们取消注释该行,C#编译器将发出以下错误:yProgram。cs(4,1):错误CS0200:属性或索引器的人员。无法将名称“”分配给--它是只读的。
private string _name = "Jane";
我们立即初始化该成员,因为稍后不可能。
public string Name { get { return _name; } }
我们通过提供get访问器使属性只读。
四、自动属性 auto-implemented properties
C#具有自动实现或自动属性。在软件项目中,有许多简单的属性只设置或获取一些简单的值。为了简化编程并缩短代码,创建了自动属性。注意,我们不能在所有情况下使用自动属性;只有简单的。
Program.cs
var p = new Person();
p.Name = "Jane";
p.Age = 17;
Console.WriteLine($"{p.Name} is {p.Age} years old");
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
这个代码要短得多。我们有一个Person类,其中有两个属性:Name和Age。
var p = new Person(); p.Name = "Jane"; p.Age = 17; Console.WriteLine($"{p.Name} is {p.Age} years old");
我们通常像往常一样使用这些属性。
public string Name { get; set; } public int Age { get; set; }
这里我们有两个自动属性。没有访问器的实现,也没有成员字段。编译器将为我们完成剩下的工作。
$ dotnet run Jane is 17 years old
五、属性体表达式(Expression body definitions)
自C#7.0以来,属性可以通过表达式体定义简化。表达式体定义由=>符号和要分配给属性或从属性检索的表达式组成。
Program.cs
var u = new User("John Doe", "gardener");
Console.WriteLine($"{u.Name} is a {u.Occupation}");
class User
{
string name;
string occupation;
public User(string name, string occupation)
{
this.name = name;
this.occupation = occupation;
}
public string Name
{
get => name;
set => name = value;
}
public string Occupation
{
get => occupation;
set => occupation = value;
}
}
在本例中,我们使用表达式体定义来定义用户类的属性。
$ dotnet run
John Doe is a gardenerl
六、其它注意事项(other notes)
我们可以使用访问修饰符标记属性,如public、private或protected。属性也可以是静态的、抽象的、虚拟的和密封的。它们的用法与常规方法相同。
Program.cs
var bs = new Base();
var dr = new Derived();
Console.WriteLine(bs.Name);
Console.WriteLine(dr.Name);
class Base
{
protected string _name = "Base class";
public virtual string Name
{
set { _name = value; }
get { return _name; }
}
}
class Derived : Base
{
protected new string _name = "Derived class";
public override string Name
{
set { _name = value; }
get { return _name; }
}
}
在前面的示例中,我们定义了一个虚拟属性,并在派生类中重写它。
public virtual string Name { set { _name = value; } get { return _name; } }
名称属性用虚拟关键字标记。
protected new string _name = "Derived class";
我们在派生类中隐藏了一个成员。为了抑制编译器警告,我们使用new关键字。
public override string Name { set { _name = value; } get { return _name; } }
这里我们重写基类的Name属性。