C# 拆分大型类共享变量:解决循环栈溢出并支持跨类调用
核心问题根源
你现在的设计从根本上搞反了类之间的继承关系,才会触发无限递归的栈溢出:
- 继承本质是「is-a」的从属关系,你现在写的
DataManageColumns : Data,等于强制把列管理类变成数据类的子类,但列管理的功能是操作数据的工具,和数据本身是「has-a」的组合关系。硬把这种工具类套上继承关系,必然会触发循环实例化:创建Data要实例化列管理类,列管理类因为继承了Data,构造时又要创建新的Data实例,链路永远停不下来,直接耗尽栈内存。 - 操作类里硬依赖
Global.CurrentData()的全局状态,进一步把数据和操作的边界搅得混乱,根本没法同时支持多份独立的CSV数据处理。
落地实现方案
用「组合替代继承」的标准设计思路,就能100%实现你想要的data.xxxManager.Method()的链式调用,完全没有循环依赖:
1. 改造Data类的基础结构
删掉之前错误加的会触发递归的internal DataManageColumns DataManageColumns = new DataManageColumns();字段,把核心数据成员的访问权限调整为同程序集可访问,在类内部实例化各个操作类时,把当前Data的引用直接注入进去:
internal class Data { private readonly int _id; protected DateTime LastUpdateTime; // 核心数据改为internal访问级别,供同程序集下的操作类读写 internal readonly List<Column> Columns = new List<Column>(); internal List<string> DataText = new List<string>(); internal string Headers; internal char Separator; // 对外暴露各个操作类的只读属性,实现链式调用 public DataManageColumns ColumnManager { get; } public DataManageSeparator SeparatorManager { get; } public DataManageHeaders HeaderManager { get; } public Data() { _id = Global.Last(); LastUpdateTime = DateTime.Now; // 实例化操作类时传入当前实例,因为操作类不继承Data,完全不会触发递归 ColumnManager = new DataManageColumns(this); SeparatorManager = new DataManageSeparator(this); HeaderManager = new DataManageHeaders(this); } public Data(List<string> dataText, char separator, bool hasHeaders) : this() { DataText = dataText; SeparatorManager.Separator = separator; if (hasHeaders) { HeaderManager.SetHeaders(); HeaderManager.ExcludeHeadersFromDataText(); ColumnManager.CreateColumns(); } } public int GetId() => _id; }
2. 改造所有操作类的逻辑
把原来操作类继承Data的写法删掉,改成在类内部持有注入的Data实例引用,完全不需要继承就能访问核心数据字段:
// 不再继承Data,从根源上消除递归实例化的可能 class DataManageColumns { // 绑定当前操作类关联的Data实例 private readonly Data _currentData; // 构造时注入绑定的Data,无额外的隐式构造调用 public DataManageColumns(Data currentData) { _currentData = currentData; } public void CreateColumns() { // 直接通过绑定的实例访问数据,不用再读全局的Global状态 for (int i = 0; i < _currentData.DataText.Count; i++) { string[] splitLine = _currentData.DataText[i].Split(_currentData.Separator); if (i == 0) { for (int j = 0; j < splitLine.Length; j++) { _currentData.Columns.Add(new Column(j, splitLine[j])); } } } } public List<Column> GetColumns() => _currentData.Columns; public void SetColumns(List<Column> newColumns) { foreach (Column column in newColumns) column.SetSerial(column.GetSerial()); } }
剩下的DataManageSeparator、DataManageHeaders全部按同样的逻辑改造:去掉对Data的继承,构造函数接收绑定的Data实例,通过注入的引用读写核心字段即可。
最终调用效果
完全满足你最初的使用预期,没有任何循环依赖、没有全局状态串数据的问题:
// 初始化一份CSV数据 var myCsvData = new Data(rawTextLines, separator: ',', hasHeaders: true); // 直接通过Data实例调用各个操作类的方法 var allColumns = myCsvData.ColumnManager.GetColumns(); // 随时修改分隔符规则 myCsvData.SeparatorManager.Separator = '\t'; // 重新解析表头 myCsvData.HeaderManager.SetHeaders();
之前尝试的方案的避坑说明
- 嵌套类实现不了的原因:嵌套类虽然可以访问外部类的私有成员,但本质还是和外部类绑定过紧,只是把代码拆分到不同文件,并没有真正做到单一职责,和partial类的问题一致,只是形式上拆分了文件,没有拆分功能边界。
- 静态类不适用的原因:静态类没有实例状态,如果你要同时处理多份独立的CSV数据,静态类存的规则会直接串到其他数据实例里,根本没法支持并行的多数据场景。
- 只要坚持让操作类继承数据类,就必然会触发递归构造——这是C#类构造的基本运行逻辑,不是代码写得是否严谨的问题,是类之间的从属关系从设计上就错了。
内容的提问来源于stack exchange,提问作者EvgenieT
相关产品推荐
相关产品推荐

