You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C#中实现Department与Employee的聚合关系并修复空引用错误

解决C#中Department与Employee聚合关系的空引用错误

问题核心

你要实现聚合关系(一名员工仅隶属于一个部门),但调用dept.GetIncentive时触发了「对象引用未设置为对象的实例」错误——本质是Employee的dept属性默认值为null,调用其方法时自然会抛出空引用异常。

错误原因

Employee类里的public Department dept { get; set; }是自动属性,C#中引用类型的默认值是null。如果创建Employee实例时没给dept赋值,后续调用dept.GetIncentive必然出错。

正确实现聚合关系+修复错误

聚合关系的核心逻辑是:员工依赖部门,但部门可以独立存在;员工必须关联一个有效的部门实例。下面提供两种可靠实现方案:

方案1:构造函数强制注入部门(推荐)

通过构造函数要求必须传入部门,从根源上避免dept为null,同时把dept设为只读属性,确保员工一旦归属某个部门就不能随意变更:

public class Employee
{
    public int fixedSalary = 50000;
    public DateTime workingFrom = new DateTime(2012, 02, 27);
    // 只读属性,确保部门关联后不可随意修改
    public Department dept { get; }

    // 构造函数强制传入部门,空值直接抛出异常
    public Employee(Department department)
    {
        dept = department ?? throw new ArgumentNullException(nameof(department), "员工必须隶属于一个部门");
    }

    // 原GetAllowance方法保持不变
    public double GetAllowance()
    {
        return GetAllowance(new DateTime(2014, 03, 31));
    }

    public double GetAllowance(DateTime cutOffDate)
    {
        double workExperience = ((cutOffDate - workingFrom).Days)/365;
        double allowance = 0;
        if (workExperience < 5)
            allowance = 0.05 * fixedSalary;
        else if(workExperience >= 5 && workExperience < 10)
            allowance = 0.1 * fixedSalary;
        else if(workExperience >= 10 && workExperience < 15)
            allowance = 0.15 * fixedSalary;
        else if (workExperience >= 15)
            allowance = 0.2 * fixedSalary;
            
        Console.WriteLine("work experience:{0}",workExperience);
        Console.WriteLine("allowance:{0}", allowance);
        return allowance;
    }

    // 原GetTotalSalary方法无需额外空值检查,因为dept必然有效
    public double GetTotalSalary(DateTime cutOffDate, float multiplyFactor)
    {            
        return fixedSalary + GetAllowance(cutOffDate) + dept.GetIncentive(multiplyFactor);
    }

    public double GetTotalSalary(float multiplyFactor)
    {
        return fixedSalary + GetAllowance() + dept.GetIncentive(multiplyFactor);
    }
}

方案2:延迟关联+空值校验(适合需后期绑定部门的场景)

如果业务允许员工先创建、再关联部门,可通过私有字段封装dept,在赋值时做空值校验,同时在调用部门方法前加检查:

public class Employee
{
    public int fixedSalary = 50000;
    public DateTime workingFrom = new DateTime(2012, 02, 27);

    private Department _dept;
    // 外部赋值时强制检查空值
    public Department dept 
    { 
        get => _dept; 
        set => _dept = value ?? throw new ArgumentNullException(nameof(value), "部门不能为null"); 
    }

    // 无参构造函数,允许先创建员工
    public Employee() { }

    // 修改GetTotalSalary方法,确保调用部门方法前已绑定部门
    public double GetTotalSalary(DateTime cutOffDate, float multiplyFactor)
    {            
        if (_dept == null)
            throw new InvalidOperationException("员工尚未关联任何部门");
        return fixedSalary + GetAllowance(cutOffDate) + _dept.GetIncentive(multiplyFactor);
    }

    public double GetTotalSalary(float multiplyFactor)
    {
        if (_dept == null)
            throw new InvalidOperationException("员工尚未关联任何部门");
        return fixedSalary + GetAllowance() + _dept.GetIncentive(multiplyFactor);
    }

    // GetAllowance方法保持不变...
}

使用示例

不管用哪种方案,创建实例时都要确保部门实例有效:

// 先创建部门实例
var techDept = new Department
{
    deptNumber = 1,
    isProducing = true,
    produce = 10000f
};

// 方案1:构造函数注入
var emp1 = new Employee(techDept);
Console.WriteLine(emp1.GetTotalSalary(0.1f));

// 方案2:后期绑定部门
var emp2 = new Employee();
emp2.dept = techDept; // 赋值null会直接抛出异常
Console.WriteLine(emp2.GetTotalSalary(0.1f));

额外优化建议

  1. 给Department类的字段加上封装(用属性代替公共字段):
public class Department
{
    public int DeptNumber { get; set; }
    public bool IsProducing { get; set; }
    public float Produce { get; set; }

    public float GetIncentive(float multiplyFactor)
    {
        return IsProducing ? Produce * multiplyFactor : 0;
    }       
}
  1. 计算工作经验时用(cutOffDate - workingFrom).TotalDays / 365.25,能更准确地考虑闰年因素。

内容的提问来源于stack exchange,提问作者Varun Gupta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 02:45:37