C#构造函数最佳实践:两种赋值方式的差异解析
两种Person类构造函数实现的差异解析
你提到微软官方文档推荐第二种构造函数实现作为最佳实践,虽然两种写法在当前代码里都能正常运行,但它们在维护性、封装性和潜在风险上有本质区别:
核心差异点
1. 代码逻辑的一致性与可维护性
- 第一种构造函数直接给私有字段
_firstName、_lastName赋值,相当于跳过了属性的访问控制逻辑。如果后续你给属性的set方法添加额外逻辑(比如参数验证、值转换、日志记录),构造函数里的赋值不会自动同步这些逻辑,你必须手动修改构造函数的代码才能覆盖到初始化场景。 - 第二种构造函数通过属性
FirstName、LastName赋值,所有对字段的修改都统一走属性的入口。不管后续属性的set逻辑怎么变化,构造函数的初始化逻辑都会自动复用这些规则,不需要额外修改,大大降低了维护成本。
2. 面向对象封装原则的遵循
- 私有字段的设计初衷就是对外隐藏内部状态,仅通过公共属性提供受控的访问入口。直接在构造函数里操作私有字段,相当于打破了类的封装边界,违背了面向对象的封装设计思想。
- 通过属性赋值则严格遵循封装原则,所有内部状态的修改都经过统一的控制层,保证类的内部状态始终符合预期的约束。
3. 潜在的无效状态风险
举个实际例子,假设后续你需要给FirstName添加非空验证:
public string FirstName { get { return _firstName; } set { _firstName = value ?? throw new ArgumentNullException(nameof(value)); } }
- 第一种构造函数直接赋值私有字段,会绕过这个验证逻辑,导致
_firstName可能被设置为null,让对象处于无效状态。 - 第二种构造函数通过属性赋值,会触发验证逻辑,在初始化阶段就阻止无效值的传入,保证对象从创建开始就是合法的。
两种实现代码对比
第一种:直接赋值私有字段
public class Person { // fields private string _firstName; private string _lastName; // data accessor public string FirstName { get { return _firstName; } set { _firstName = value; } } public string LastName { get { return _lastName; } set { _lastName = value; } } // constructor public Person(string fn, string ln) { _firstName = fn; _lastName = ln; } }
第二种:通过属性赋值(推荐)
public class Person { // fields private string _firstName; private string _lastName; // data accessor public string FirstName { get { return _firstName; } set { _firstName = value; } } public string LastName { get { return _lastName; } set { _lastName = value; } } // constructor public Person(string fn, string ln) { FirstName = fn; LastName = ln; } }
内容的提问来源于stack exchange,提问作者user10242552
相关产品推荐
相关产品推荐

