C#中的Property属性

生成模型:QwQ-32B,生成时间:2025 03-18 20:57

在 C# 中,属性(Property) 是一种用于封装类或结构体的字段(Field)的机制,提供对字段的 访问和修改 控制。属性通过 getset 访问器(Accessor)实现,允许在获取或设置字段值时添加逻辑(如验证、计算或日志记录),同时隐藏字段的直接访问,从而实现 数据封装 安全性

属性的基本概念

  • 作用: 属性是类的成员,用于以更安全、灵活的方式访问或修改字段的值。
  • 对比字段: 直接暴露字段(如 public int Age)可能导致数据不一致或安全风险,而属性通过 get / set 控制访问。

属性的语法

基本结构

public class MyClass
{
    // 私有字段(BackingField)
    private int age;

    // 属性
    public int Age
    {
        get { return age; }      // 获取值
        set { age = value; }    // 设置值
    }
}

简化写法(自动属性)

C# 允许用更简洁的语法定义属性,编译器会自动生成私有字段:

public int Age { get; set; }

属性的组成部分

  1. get 访问器: 定义如何获取属性的值。
    • 返回类型必须与属性声明的类型一致。
    • 可以包含逻辑(如计算或验证)。
  2. set 访问器: 定义如何设置属性的值。
    • 接收一个参数 value(隐式参数名,不可修改)。
    • 可以包含验证逻辑(如范围检查)。

示例:带验证的属性

需求:确保年龄(Age)始终为非负数。

public class Person
{
    // 私有字段
    private int age;

    // 属性:Age
    public int Age
    {
        get { return age; } // 获取 age 的值

        set 
        {
            if (value < 0)
            {
                throw new ArgumentOutOfRangeException("年龄不能为负数");
            }
            age = value; // 设置 age 的值
        }
    }
}

使用示例

var person = new Person();
person.Age = 25;    // 正常设置
Console.WriteLine(person.Age); // 输出:25

person.Age = -10;   // 抛出异常:ArgumentOutOfRangeException

自动属性(Auto-Implemented Properties)

C# 允许通过 get / set 的简化语法定义属性,无需显式声明私有字段。

public class Person
{
    // 自动属性:编译器自动生成私有字段(如 <Age>k__BackingField)
    public string Name { get; set; }
    public int Age { get; set; }
}

初始化自动属性

var person = new Person { Name = "Alice", Age = 30 };

只读属性(Read-Only Property)

  • 属性只能读取,不能写入。
  • 通常用于需要初始化或计算的场景。
public class Person
{
    public string Name { get; } // 只读属性

    // 通过构造函数初始化
    public Person(string name)
    {
        Name = name; // 在构造函数中设置
    }
}

使用示例

var person = new Person("Bob");
Console.WriteLine(person.Name); // 输出:Bob
// person.Name = "Alice"; // 报错:属性没有 set 访问器

依赖属性(Dependent Properties)

属性的值可以依赖其他字段或属性。例如,计算全名(FullName):

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

    // 依赖属性:FullName
    public string FullName
    {
        get { return $"{FirstName} {LastName}"; }
    }
}

使用示例

var person = new Person { FirstName = "John", LastName = "Doe" };
Console.WriteLine(person.FullName); // 输出:John Doe

索引器(Indexer)

索引器是一种特殊属性,允许通过索引(如 [])访问对象的元素。

public class MyCollection
{
    private string[] items = new string[10];

    // 索引器
    public string this[int index]
    {
        get { return items[index]; }
        set { items[index] = value; }
    }
}

使用示例

var collection = new MyCollection();
collection[0] = "Hello"; // 调用 set
Console.WriteLine(collection[0]); // 调用 get,输出:Hello

属性的高级用法

不同访问修饰符

可以为 getset 设置不同的访问权限:

public int Age { get; private set; } // 外部只能读取,内部可以设置

表达式主体(Expression-Bodied Properties)

C# 6.0+ 允许用简化语法:

public string Name { get; set; }
public string FullName => $"{FirstName} {LastName}"; // 表达式主体属性

与接口配合

属性可以定义在接口中,由实现类提供具体逻辑:

public interface IAnimal
{
    string Name { get; set; }
}

public class Dog : IAnimal
{
    public string Name { get; set; }
}

属性 vs 字段

特性 属性(Property) 字段(Field)
访问控制 可以通过 get / set 控制访问 直接暴露,无法控制访问权限
封装性 提供封装,隐藏内部实现细节 直接暴露数据,破坏封装性
验证逻辑 可以添加验证(如 if (value < 0) 无法直接添加验证
计算值 可以返回计算结果(如 FullName 仅存储原始数据
默认值 自动属性会初始化为默认值(如 0, null 需要显式初始化

总结

  • 属性是 C# 中封装字段的最佳实践,通过 get / set 提供对字段的受控访问。
  • 自动属性简化了代码,适合简单场景。
  • 验证和计算逻辑 可以通过属性实现,增强代码的健壮性和灵活性。
  • 避免直接暴露字段,以提高代码的可维护性和安全性。

通过以上示例和解释,你应该能够理解属性的基本用法和核心价值。如果需要更复杂的场景(如延迟加载、通知属性),可以进一步探索 INotifyPropertyChanged 或其他设计模式。

实例

using System;
namespace runoob
{
   class Student
   {

      private string code = "N.A";
      private string name = "not known";
      private int age = 0;

      // 声明类型为 string 的 Code 属性
      public string Code
      {
         get
         {
            return code;
         }
         set
         {
            code = value;
         }
      }
   
      // 声明类型为 string 的 Name 属性
      public string Name
      {
         get
         {
            return name;
         }
         set
         {
            name = value;
         }
      }

      // 声明类型为 int 的 Age 属性
      public int Age
      {
         get
         {
            return age;
         }
         set
         {
            age = value;
         }
      }
      public override string ToString()
      {
         return "Code = " + Code +", Name = " + Name + ", Age = " + Age;
      }
    }
    class ExampleDemo
    {
      public static void Main()
      {
         // 创建一个新的 Student 对象
         Student s = new Student();
            
         // 设置 student 的 code、name 和 age
         s.Code = "001";
         s.Name = "Zara";
         s.Age = 9;
         Console.WriteLine("Student Info: {0}", s);
         // 增加年龄
         s.Age += 1;
         Console.WriteLine("Student Info: {0}", s);
         Console.ReadKey();
       }
   }
}

输出:

Student Info: Code = 001, Name = Zara, Age = 9
Student Info: Code = 001, Name = Zara, Age = 10

使用抽象类的抽象属性:

using System;
namespace Runoob
{
    public abstract class Person
    {
        public abstract string Name { get; set; }
        public abstract int Age { get; set; }
    }

    class Student : Person
    {
        // 声明自动实现的属性
        public string Code { get; set; } = "N.A";
        public override string Name { get; set; } = "N.A";
        public override int Age { get; set; } = 0;

        public override string ToString()
        {
            return $"Code = {Code}, Name = {Name}, Age = {Age}";
        }
    }

    class ExampleDemo
    {
        public static void Main()
        {
            // 创建一个新的 Student 对象
            Student s = new Student
            {
                Code = "001",
                Name = "Zara",
                Age = 9
            };

            Console.WriteLine("Student Info:- {0}", s);
            
            // 增加年龄
            s.Age += 1;
            Console.WriteLine("Student Info:- {0}", s);

            Console.ReadKey();
        }
    }
}

输出是相同的。