Unity/C#中ScriptableObject作数据容器的类继承最佳实践咨询
解决方案
你当前的写法里Manager没有继承Employee,而是直接继承了MonoBehaviour,才会出现重复定义数据容器字段的问题。以下是符合继承逻辑、避免重复引用的3种常用方案:
方案1:泛型基类(最推荐,兼顾类型安全和灵活性)
把基类定义为带约束的泛型类,直接限定数据容器的类型,子类只要传入对应的SO类型即可,不需要重复定义字段,全程无多余引用。
// 泛型基类,约束T必须是EmployeeSO类型 public class Employee<T> : MonoBehaviour where T : EmployeeSO { [SerializeField] protected T _dataSO; // 基类可以直接访问T中属于EmployeeSO的所有字段,比如employeeName } // 普通员工类,传入EmployeeSO作为泛型参数 public class NormalEmployee : Employee<EmployeeSO> { // 不需要额外定义数据字段,直接用基类的_dataSO即可 } // 经理类,传入继承自EmployeeSO的ManagerSO作为泛型参数 public class Manager : Employee<ManagerSO> { public void Test() { // 直接访问基类的_dataSO,既可以拿到父类的employeeName,也可以拿到ManagerSO独有的role Debug.Log(_dataSO.employeeName); Debug.Log(_dataSO.role); } } // SO层的继承保持不变 public class EmployeeSO : ScriptableObject { public string employeeName; } public class ManagerSO : EmployeeSO { public string role; }
优势:类型安全,不需要强制转换,Inspector面板只会显示一个对应类型的_dataSO字段,不会出现误赋值的问题
方案2:非泛型方案(适配不适合用泛型的场景)
如果基类需要被当作普通非泛型类使用(比如要存入统一的员工列表),可以把基类的SO字段用属性封装,子类只做类型转换即可,同时隐藏基类的序列化字段避免误操作。
public class Employee : MonoBehaviour { // 把基类的SO字段隐藏,不在Inspector显示,避免手动赋值 [SerializeField, HideInInspector] protected EmployeeSO _employeeSO; // 对外暴露的只读属性,基类逻辑都用这个属性访问数据 public virtual EmployeeSO DataSO => _employeeSO; } public class Manager : Employee { // 子类只定义自己的ManagerSO序列化字段 [SerializeField] protected ManagerSO _managerSO; // 重写属性,返回子类的SO同时同步给基类引用,保证基类逻辑正常运行 public override EmployeeSO DataSO { get { _employeeSO = _managerSO; return _managerSO; } } public void Test() { // 子类直接用自己的_managerSO访问独有属性 Debug.Log(_managerSO.role); // 基类逻辑访问DataSO就能拿到统一的员工通用数据 Debug.Log(DataSO.employeeName); } }
优势:兼容非泛型的使用场景,Inspector只会显示Manager类的_managerSO字段,不会出现重复字段误导操作
方案3:抽象基类隔离(适合需要严格分层的场景)
如果要彻底隔离基类和子类的数据实现,可以把基类改成抽象类,只定义数据的访问接口,不持有具体的SO引用,所有数据持有都交给子类实现,完全不会有多余引用残留。
public abstract class Employee : MonoBehaviour { // 抽象属性,基类只约定要能拿到EmployeeSO,不做具体实现 public abstract EmployeeSO DataSO { get; } // 基类所有逻辑都通过DataSO访问员工通用数据 public void PrintName() { Debug.Log(DataSO.employeeName); } } public class NormalEmployee : Employee { [SerializeField] private EmployeeSO _employeeSO; public override EmployeeSO DataSO => _employeeSO; } public class Manager : Employee { [SerializeField] private ManagerSO _managerSO; public override EmployeeSO DataSO => _managerSO; public void PrintRole() { Debug.Log(_managerSO.role); } }
优势:基类完全不关心子类的具体数据实现,彻底避免引用不一致的问题
内容的提问来源于stack exchange,提问作者Eyap
相关产品推荐
相关产品推荐

