CodeGym /课程 /C# SELF /自动属性和init-only setter

自动属性和init-only setter

C# SELF
第 17 级 , 课程 2
可用

1. 自动属性

在C#里,属性(property)其实就是字段和方法的混合体。说白了,就是语法糖,让你用起来更爽更方便封装。你用起来像字段,实际上里面可以塞各种逻辑:校验、改别的字段、调用方法,甚至给你奶奶发邮件(最后这个别学)。上一节我们还在手写属性,但如果你有一堆超简单的对象,只要“getter”和“setter”,写多了真的腻。

来,认识一下——自动属性。它能帮你省掉模板代码,让C#自己搞定存值的事,你不用操心。

以前怎么写:手动属性

想把字段藏起来,但又想通过属性访问,通常这么写:


public class Dog
{
    private string name;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }
}
手动属性+私有字段

这种代码就是典型的“啰嗦模板”(boilerplate)。其实大多数时候get/set里啥都不用加!只要能访问就行。

现在可以这样

C#现在更简单了:不用手写字段,编译器帮你搞定。你只要写:


public class Dog
{
    public string Name { get; set; }
}
自动属性

就搞定了。你可以直接读写Name,编译器会在背后自动生成一个私有字段,外部根本拿不到。

幕后到底发生了啥?当你写public string Name { get; set; },编译器会自动生成一个隐藏字段,比如Name__BackingField。每次访问Name,它会插入对应的getter/setter。你在代码里看不到这个字段,但它确实存在——用反射还能找到(不过那是另一个故事了)。

这种写法有啥爽点?首先,代码短多了,不用一直重复。其次,代码更清爽:少点视觉噪音,读起来更舒服。最后,更安全——没人能直接乱动字段,必须通过属性来搞。

2. 自动属性语法

语法超级简单:


public class Dog
{
    public string Name { get; set; }
    public int Age { get; set; }
}
多个自动属性

现在我们的Dog看起来简洁多了!你可以像平时那样创建对象和赋值:

Dog myDog = new Dog();
myDog.Name = "巴尔博斯";
myDog.Age = 4;

图解

方式 语法 值的访问
字段
public string Name;
直接访问
手动属性
public string Name { get; set; } + private string name;
通过get/set访问
自动属性
public string Name { get; set; }
get/set + 隐藏字段

3. 访问控制:只读、只写

只读:get-only

如果属性只需要读(比如宠物的注册编号),可以只写getset可以不写,或者设成私有。这样只能在构造函数或声明时赋值:


public class Dog
{
    public string RegistrationCode { get; }

    public Dog()
    {
        RegistrationCode = "DOG-001"; // 构造函数里赋值
    }
}

现在外部只能读取RegistrationCode,不能改。很适合保护重要数据不被误改。

另一种写法:私有set


public string Name { get; private set; }
私有setter——只能在类内部改

这样属性只能在类内部(比如方法或构造函数)改。

只写:不推荐

理论上可以做set-only属性(只能写),但极少见也不推荐——想象一下一个“黑洞”对象:你能往里扔东西,但永远拿不回来。

4. init-only setter

小历史

以前想做不可变对象(immutable),只能写get属性,然后在构造函数里赋值。还行,但有点麻烦。最近C#出了新语法:init-only属性。

init setter到底是啥?

  • 只能在创建对象时赋值(也就是构造函数对象初始化器)。
  • 对象创建后,属性就变成只读。
  • 很适合“几乎不可变”的类、DTO、配置、模型啥的。

public class Dog
{
    public string Name { get; init; }
    public int Age { get; init; }
}
init setter的属性

现在你可以这样写:

Dog dog = new Dog { Name = "鲍比克", Age = 2 };
dog.Name = "雷克斯"; // 报错! 创建后只能读

构造函数里也能赋值:

public Dog(string name, int age)
{
    Name = name;
    Age = age;
}

图表:啥时候能赋值

赋值位置 { get; set; } { get; private set; } { get; init; }
类外,创建后
构造函数
初始化器
类内部 ❌(除了构造函数)

5. 实战应用

来,把Dog类用自动属性和init-only属性重写下:


public class Dog
{
    // 只能初始化时赋值的属性
    public string Name { get; init; }
    public int Age { get; init; }

    // 自动属性,当前状态
    public bool IsHungry { get; set; }

    // 方法,狗的动作
    public void Bark()
    {
        Console.WriteLine($"{Name}说:汪!");
    }
}

现在你可以这样创建Dog:

Dog dog = new Dog { Name = "沙里克", Age = 3, IsHungry = true };

dog.Bark(); // 输出:沙里克说:汪!
dog.IsHungry = false; // 这个属性可以随时改
dog.Name = "巴尔博斯"; // 报错!只能初始化时赋值

6. 自动属性的初始值

C#就是贴心。你甚至可以直接给属性设初始值:


public class Dog
{
    public string Name { get; set; } = "无名";
    public int Age { get; set; } = 0;
}
带初始值的自动属性

或者配合init


public class Dog
{
    public string Name { get; init; } = "小狗";
    public int Age { get; init; } = 0;
}
带初始值的init-only属性

现在如果创建时没指定Name,它就是“小狗”。

7. 自动属性的访问级别

你可以让属性的读写权限不一样,比如:


public class Dog
{
    public string Name { get; private set; }
    public int Age { get; private set; }
    
    public Dog(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

这样只有Dog类内部(比如方法或构造函数)能改名字和年龄。

手动属性VS自动属性对比

方式 写起来难度 set/get控制 创建后能不能改 模型里用
手动属性 啰嗦 完全可控 随便 需要逻辑时
自动属性 简短 不可控 随便或私有 几乎都能用
init-only自动属性 简短 不可控 只能创建时赋值 DTO、配置

8. 用属性常见的坑

坑1:试图在构造函数外给get-only属性赋值。
如果属性只有get,编译器不让你在别的地方赋值。只能在构造函数或声明时赋。忘了就编译报错。

坑2:字段和属性搞混了。
把字段换成自动属性时,要确保别的代码没直接用字段。字段和属性不是一回事,老代码没改会报错或者程序行为很迷。

坑3:私有set没搞明白。
如果属性有私有set,但别的地方还想赋值——会报错。经常是复制别人的代码时不小心。一定要确认set在你用的地方能不能用。

2
任务
C# SELF, 第 17 级, 课程 2
已锁定
使用 init-only 属性
使用 init-only 属性
2
任务
C# SELF, 第 17 级, 课程 2
已锁定
带有初始值的自动属性
带有初始值的自动属性
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION