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# 技术干货!如果觉得有用,记得收藏本文!

相关推荐
程序leo源19 小时前
Qt窗口详解
开发语言·数据库·c++·qt·青少年编程·c#
掘金一周20 小时前
想换一辆电车,JYM有什么推荐 | 沸点周刊 5.21
前端·人工智能·后端
Nian.Baikal20 小时前
Cesium 3D Tiles 加载与优化实战
前端·cesium
KaMeidebaby20 小时前
卡梅德生物技术快报|噬菌体肽库展示技术构建 Mhp168‑Hsp70 定向随机肽库:流程、质控与数据结果
前端·数据库·其他·百度·新浪微博
lchcy20 小时前
前端实现单点登录(SSO登录)
前端
卷帘依旧21 小时前
SPA下的路由模式详解
前端
环信21 小时前
2026年开发者选择即时通讯厂商应注意的几点
前端
卷帘依旧21 小时前
Generator 全面解析 + async/await 深度对比
前端·javascript
yqcoder1 天前
数据劫持的双雄:深入解析 Object.defineProperty 与 Proxy
开发语言·前端·javascript
lichenyang4531 天前
鸿蒙聊天 Demo 练习 03:接入 Next.js 后端接口,实现真机前后端联调
前端