C# 自动属性初始化:TOP-5 错误与大师课

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么自动属性问题吸引了数千次浏览?

\n\n

如果你曾经写过 C# 代码,那么你很可能遇到过这样一个简单但重要的问题:"如何正确地为自动属性设置初始值?"。这个问题在 Stack Overflow 上吸引了数万次浏览,这并非没有原因。看似简单的操作背后隐藏着许多陷阱。

\n\n

想象一下:你创建了一个 User 类,希望 Name 属性的默认值不是 null,而是一个空字符串。看起来能出什么问题?但在实践中,开发者常常犯下 C# 属性初始化的典型错误,导致 NullReferenceException 或难以理解的行为。

\n\n

在本文中,我们将分析 TOP-5 错误,并展示如何避免它们。你将了解从 C# 6 到 C# 12 的现代方法,并明白哪种方式最适合你的项目。

\n\n
\n\n

错误 #1:忽略自动属性的初始化

\n\n

最常见的错误是 不为引用类型设置初始值。许多新手会这样写:

\n\n
public class User\n{\n    public string Name { get; set; } // 默认为 null\n    public List<string> Roles { get; set; } // 默认为 null\n}
\n\n

在 C# 中,引用类型的自动属性默认值为 null。这会导致 NullReferenceException 问题,当你忘记在使用前赋值时就会发生。

\n\n

如何修复?

\n

始终显式设置 C# 自动属性初始化器。最简单的方法是使用 C# 6 的语法:

\n\n
public class User\n{\n    public string Name { get; set; } = string.Empty;\n    public List<string> Roles { get; set; } = new List<string>();\n}
\n\n
\n

建议:使用 string.Empty 而不是空引号——这能提高可读性,并符合 C# 最佳实践。

\n
\n\n
\n\n

错误 #2:在构造函数中初始化而不考虑调用链

\n\n

另一个常见错误是 仅在构造函数中设置 C# 默认值,而忽略了属性初始化器。这可能导致混淆,尤其是在继承场景中。

\n\n
public class BaseClass\n{\n    public int Value { get; set; }\n\n    public BaseClass()\n    {\n        Value = 10;\n    }\n}\n\npublic class DerivedClass : BaseClass\n{\n    public DerivedClass()\n    {\n        // Value 已经是 10,但我们可以覆盖它\n    }\n}
\n\n

问题:如果你在 Value 属性上添加了初始化器 并且 在构造函数中保留了赋值操作,那么执行顺序可能会让你惊讶。初始化器在基类构造函数体 之前 执行,但在派生类构造函数调用 之后 执行。

\n\n

如何避免?

\n

选择一种方式:要么用初始化器,要么用构造函数。最好优先使用 C# 自动属性初始化器,因为它们更可预测且更简洁。

\n\n
public class BaseClass\n{\n    public int Value { get; set; } = 10; // 单一事实来源\n}
\n\n
\n\n

错误 #3:使用 readonly 字段代替自动属性

\n\n

一些开发者在尝试设置初始值时,放弃使用自动属性,转而使用公共字段:

\n\n
public class Config\n{\n    public readonly int MaxRetries = 3; // 错误:破坏了封装性\n}
\n\n

这是一种不良实践。字段破坏了 封装性——你无法在不更改 API 的情况下为 getter/setter 添加逻辑。此外,在序列化或使用 WPF/Blazor 时,这会产生问题。

\n\n

正确方法

\n

使用带初始化器的自动属性,或者使用 C# 9 的 init-only 属性:<

博客

书籍推荐