C#彻底搞懂属性(Property)与字段(Field)

一、字段(Field)

字段是类或结构中直接声明的变量。它的唯一使命,就是作为对象状态的一部分,存储数据。

1. 字段的声明

csharp 复制代码
public class Student
{
    // 这就是两个字段
    public string Name; // 学生姓名
    public int Age;     // 学生年龄
}

2. 访问修饰符 -- Public

csharp 复制代码
Student stu = new Student();
stu.Name = "张三";
stu.Age = -10; // 灾难!年龄怎么可能是负数?

Console.WriteLine($"{stu.Name}的年龄是 {stu.Age}岁。");
// 输出: 张三的年龄是 -10岁。
  • 使用Public可以在任何时候,任何地方直接访问,并且修改,这会存在某些隐患

3. 访问修饰符 -- Private

  • Getter/Setter方法模式
csharp 复制代码
public class Student
{
    // 将字段设为private,外界无法直接访问
    private string _name;
    private int _age;

    // 为了让外界能设置和获取值,我们提供一对公开的方法
    public string GetName()
    {
        return _name;
    }

    public void SetName(string name)
    {
        // 可以在这里加入验证逻辑
        if (string.IsNullOrWhiteSpace(name))
        {
            Console.WriteLine("姓名不能为空!");
            return;
        }
        _name = name;
    }

    public int GetAge()
    {
        return _age;
    }

    public void SetAge(int age)
    {
        // 验证逻辑!
        if (age < 0 || age > 150)
        {
            Console.WriteLine("年龄不合法!");
            return;
        }
        _age = age;
    }
}

// === 使用 ===
Student stu = new Student();
stu.SetName("李四");
stu.SetAge(-10); // 输出: 年龄不合法!
stu.SetAge(25);

Console.WriteLine($"{stu.GetName()}的年龄是 {stu.GetAge()}岁。");

二、属性(Property)

属性是字段的自然演进。它为私有字段提供了一个公开的、受控的访问层,但在语法上,它看起来就像一个公有字段。它巧妙地结合了方法的灵活性和字段的简洁性。

1. 属性的完整声明

一个完整的属性包含一个私有的字段 和一对get/set访问器(Accessor)

csharp 复制代码
public class Student
{
    // 1. 私有字段,用于实际存储数据
    private int _age;

    // 2. 公开属性,作为外界访问的入口
    public int Age
    {
        // 3. get访问器:当读取属性时执行
        get
        {
            // 可以加入逻辑,比如权限检查
            return _age;
        }

        // 4. set访问器:当给属性赋值时执行
        set
        {
            // 'value'是一个上下文关键字,代表赋过来的值
            if (value < 0 || value > 150)
            {
                // 可以抛出异常,这是更推荐的做法
                throw new ArgumentOutOfRangeException(nameof(Age), "年龄必须在0到150之间。");
            }
            _age = value;
        }
    }
}

// === 使用属性 ===
Student stu = new Student();
try
{
    stu.Age = 25; // 调用set访问器,value为25
    Console.WriteLine(stu.Age); // 调用get访问器

    stu.Age = -10; // 调用set访问器,value为-10,抛出异常
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

三、属性的进阶 - 语法糖

C#语言在不断发展,属性的写法也越来越简洁。

1. 自动实现属性(Auto-Implemented Properties)

在很多情况下,我们的属性不需要任何特殊的验证逻辑,只是简单地存取一个值。为这种情况手写字段和get/set代码块显得很冗余。于是,C# 引入了自动属性。

csharp 复制代码
public class Product
{
    // 这就是自动属性
    // 编译器会自动在幕后创建一个私有的、匿名的字段
    public string Name { get; set; }
    public decimal Price { get; set; }
}

// === 使用 ===
Product phone = new Product();
phone.Name = "Phone X"; // 背后调用了自动生成的set
phone.Price = 999.99m;

这是我们日常开发中最常用的形式。简洁、高效。只有当你需要添加自定义逻辑时,才需要退回到手动实现字段的方式。

2. 控制可访问性

我们可以为getset访问器设置不同的访问级别。

csharp 复制代码
public class Order
{
    // Id只能在类的内部被设置(比如在构造函数中)
    // 但可以在任何地方被读取
    public int Id { get; private set; }

    public DateTime OrderDate { get; } // 只有get,这是一个只读属性

    public Order(int id)
    {
        this.Id = id; // 在内部设置是合法的
        this.OrderDate = DateTime.UtcNow; // 只读属性只能在构造函数或声明时赋值
    }
}

3. 表达式主体属性

对于一些只读的、由其他数据计算得出的属性,C# 提供了更简洁的写法。

csharp 复制代码
public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    // 这个只读属性的值是计算出来的
    public string FullName => $"{FirstName} {LastName}";
}

四、C#中的特殊属性

1. init访问器

init 访问器让属性拥有了特殊的"只写一次"能力。它允许属性在对象初始化器中被赋值,但在那之后就变成只读。

csharp 复制代码
public class Book
{
    public string Isbn { get; init; } // 注意是init
    public string Title { get; init; }
}

// === 使用 ===
var book = new Book
{
    Isbn = "978-0321765723", // 合法!在对象初始化器中赋值
    Title = "The C# Programming Language"
};

// book.Title = "New Title"; // 编译错误!初始化完成后,不能再修改

2. required修饰符

创建对象时必须为某个属性赋值。

csharp 复制代码
public class User
{
    public int Id { get; set; }
    public required string Username { get; set; } // 必须在创建时提供值
    public string? Email { get; set; } // 可选
}

// === 使用 ===
// var user1 = new User(); // 编译错误!Username是required的,但没有被赋值。
var user2 = new User { Username = "alice" }; // 正确

结语

点个赞,关注我获取更多实用 C# 技术干货!如果觉得有用,记得收藏本文!

相关推荐
黑棠会长3 小时前
微服务实战.06 |微服务对话时,你选择打电话还是发邮件?
微服务·云原生·架构·c#
GGGG寄了4 小时前
CSS——CSS引入方式+选择器类型
前端·css·html
墨染青竹梦悠然4 小时前
基于Django+vue的图书借阅管理系统
前端·vue.js·后端·python·django·毕业设计·毕设
码农六六4 小时前
js函数柯里化
开发语言·前端·javascript
爱敲代码的小鱼4 小时前
Vue的简介:
前端·javascript·vue.js
xb11324 小时前
C#串口通信
开发语言·c#
H_ZMY4 小时前
前端瀑布流布局:从基础实现到高性能优化全解析
前端·性能优化
周杰伦fans4 小时前
CAD二次开发中的线程、异步操作与LockDocument
c#
星夜落月4 小时前
从零部署Wallos:打造专属预算管理平台
服务器·前端·网络·建站