ZetCode

C# 属性

最后修改时间:2024 年 1 月 31 日

在本文中,我们将展示如何在 C# 中使用属性。

属性是一个成员,它提供了一种灵活的机制来读取、写入或计算私有字段的值。

属性使用访问器,通过访问器可以读取、写入或操作私有字段的值。 属性的读取和写入被转换为 get 和 set 方法调用。 属性在提供方便的字段访问的同时,将数据与外界隔离。

get 属性访问器用于返回属性值,set 属性访问器用于分配新值。 init 属性访问器仅在对象构造期间用于分配新值。 value 关键字用于定义由 setinit 访问器分配的值。

属性可以是读写(它们同时具有 get 和 set 访问器)、只读(它们只有 get 访问器)或只写(它们只有 set 访问器)。

带有后备字段的 C# 属性

以下示例使用带有后备字段的属性。

Program.cs
User u = new();
u.Name = "Jane";

Console.WriteLine(u.Name);

class User
{
    private string? _name;

    public string? Name
    {
        get { return _name; }
        set { _name = value; }
    }
}

我们有带有 _name 后备字段的 Name 属性。

User u = new();
u.Name = "Jane";

Console.WriteLine(u.Name);

我们创建一个 User 类的实例。 我们使用字段表示法访问成员字段。

public string? Name
{
   ...
}

我们有一个名为 Name 的属性。 它看起来像一个常规的方法声明。 区别在于它具有称为 getset 的特定访问器。

get { return _name; }
set { _name = value; }

get 属性访问器用于返回属性值,set 访问器用于分配新值。 value 关键字用于定义由 set 访问器分配的值。

C# 只读属性

要创建只读属性,我们省略 set 访问器,只在实现中提供 get 访问器。

Program.cs
var u = new User("John Doe", "gardener");
Console.WriteLine(u);

class User(string name, string occupation)
{
    public string Name
    {
        get { return name; }
    }

    public string Occupation
    {
        get { return occupation; }
    }

    public override string ToString()
    {
        return $"{name} is a {occupation}";
    }
}

在该示例中,我们有只读属性。 一旦在构造函数中初始化,它们就无法修改。

public string Name
{
    get { return name; }
}

我们通过只提供 get 访问器使属性成为只读。

C# 自动实现的属性

C# 具有自动实现或自动属性。 使用自动属性,编译器会透明地为我们提供后备字段。

Program.cs
var u = new User();
u.Name = "John Doe";
u.Occupation = "gardener";

Console.WriteLine($"{u.Name} is a {u.Occupation}");

class User
{
    public string? Name { get; set; }
    public string? Occupation { get; set; }
}

此代码要短得多。 我们有一个 User 类,其中有两个属性:NameOccupation

var u = new User();
u.Name = "John Doe";
u.Occupation = "gardener";

Console.WriteLine($"{u.Name} is a {u.Occupation}");

我们通常像往常一样使用这些属性。

public string? Name { get; set; }
public string? Occupation { get; set; }

这里我们有两个自动属性。 没有访问器的实现,也没有成员字段。 编译器将为我们完成剩下的工作。

$ dotnet run
John Doe is a gardener

C# init-only 属性

init 关键字用于创建 init-only 属性;这些属性只能在对象构造期间初始化。

Program.cs
var u = new User("John Doe", "gardener");
Console.WriteLine(u);

class User(string name, string occupation)
{
    public string Name { get; init; } = name;
    public string Occupation { get; init; } = occupation;

    public override string ToString()
    {
        return $"{Name} is a {Occupation}";
    }
}

我们定义两个 init-only 属性;它们在对象构造时初始化。 之后,它们变为不可变的。

class User(string name, string occupation)
{
   ...
}

我们使用主构造函数。 它为我们提供了两个参数:nameoccupation。 它们稍后用于初始化属性。

public string Name { get; init; } = name;
public string Occupation { get; init; } = occupation;

init-only 属性是使用 init 关键字创建的。

C# required 属性

required 关键字用于强制客户端实现该属性。

Program.cs
var u = new User { Name = "John Doe", Occupation = "gardener" };
Console.WriteLine(u);

var u2 = new User { Name = "Roger Roe" };
Console.WriteLine(u2);


class User
{
    public required string Name { get; init; }
    public string? Occupation { get; init; }

    public override string ToString()
    {
        return $"User{{ {Name} {Occupation} }}";
    }
}

我们必须使用对象初始值设定项来创建具有 required 属性的对象。

$ dotnet run
User{ John Doe gardener }
User{ Roger Roe  }

C# 表达式体定义

属性可以使用表达式体定义来简化。 表达式体定义由 => 符号以及要分配给属性或从属性检索的表达式组成。

Program.cs
var u = new User("John Doe", "gardener");
Console.WriteLine($"{u.Name} is a {u.Occupation}");

class User
{
    private string _name;
    private string _occupation;

    public User(string name, string occupation)
    {
        Name = name;
        Occupation = occupation;
    }

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

    public string Occupation
    {
        get => _occupation;
        set => _occupation = value;
    }
}

在该示例中,我们使用表达式体定义来为 User 类定义属性。

$ dotnet run
John Doe is a gardener

其他说明

我们可以使用访问修饰符(如 publicprivateprotected)标记属性。 属性也可以是 staticabstractvirtualsealed。 它们的用法与常规方法相同。

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

在前面的示例中,我们定义了一个虚拟属性,并在 Derived 类中覆盖它。

public virtual string Name
{
    set { _name = value; }
    get { return _name; }
}

Name 属性用 virtual 关键字标记。

protected new string _name = "Derived class";

我们正在隐藏 Derived 类中的成员。 为了抑制编译器警告,我们使用 new 关键字。

public override string Name
{
    set { _name = value; }
    get { return _name; }
}

在这里,我们覆盖了 Base 类的 Name 属性。

来源

属性 - 编程指南

在本文中,我们介绍了 C# 属性。

作者

我叫 Jan Bodnar,我是一位热情的程序员,拥有丰富的编程经验。 我自 2007 年以来一直在撰写编程文章。 迄今为止,我已经撰写了超过 1,400 篇文章和 8 本电子书。 我在编程教学方面拥有超过十年的经验。

列出所有 C# 教程