C#能否自定义类数组的变量集合?数组是否具特殊性?
数组与其他集合的本质差异:变量集合 vs 值集合
我一直以为C#里的数组和其他集合并没区别,只是个可变但固定大小的集合。但深入研究后发现,数组本质完全不同——它是变量的集合,而非值的集合。看下面的代码示例:
var array = new MyStruct[] { new MyStruct() }; var list = new List<MyStruct> { new MyStruct() }; array[0].Value = 1; // 合法,直接修改数组中元素的成员 list[0].Value = 2; // 非法,编译报错:无法修改返回值,因为它不是变量 var listElement = list[0]; listElement.Value = 3; // 合法,但只是修改了副本,原List中的元素不受影响 struct MyStruct // 可变结构体 { public int Value; }
基于这个现象,我有两个技术问题:
- 能不能在C#中编写自定义集合,模拟数组的行为——作为变量的集合而非值的集合?
- 如果无法实现,是不是意味着数组在这个特性上有特殊性,和其他所有集合本质不同?
问题1:可以实现,但依赖C#的ref返回特性
从C# 7.0开始,支持ref返回值的索引器,借助这个特性可以自定义出类似数组行为的集合。比如我们可以写一个RefList<T>,让它的索引器返回ref T,这样就能直接修改值类型元素的成员:
public class RefList<T> where T : struct { private T[] _innerArray; public int Count { get; private set; } public RefList(int capacity = 4) { _innerArray = new T[capacity]; Count = 0; } public void Add(T item) { if (Count == _innerArray.Length) Array.Resize(ref _innerArray, _innerArray.Length * 2); _innerArray[Count] = item; Count++; } // 返回ref T的索引器 public ref T this[int index] { get { if (index < 0 || index >= Count) throw new IndexOutOfRangeException(); return ref _innerArray[index]; } } }
使用这个集合时,就能像数组一样直接修改值类型成员:
var refList = new RefList<MyStruct>(); refList.Add(new MyStruct()); refList[0].Value = 4; // 合法,直接修改集合中元素的成员
不过这种实现有几个限制:
- 仅适用于值类型(引用类型本身就是引用,修改成员不需要ref返回)
- 索引器的ref返回不能用于部分场景,比如迭代器中,也不能直接赋值给非ref变量(除非用
ref var声明)
问题2:数组确实有CLR层面的特殊性
数组的这种“直接修改值类型成员”的行为,是CLR原生支持的特殊特性——哪怕在C# 7.0还没引入ref返回的时期,数组的索引器就已经允许这种操作。这是因为数组在CLR中是一种特殊类型,它的索引访问直接对应内存地址的引用,而非返回值的副本。
而自定义集合只能通过C#的ref返回语法模拟这种行为,本质是借助语言特性复刻CLR对数组的原生支持。所以从底层实现来看,数组在这个特性上确实和普通集合有本质区别——它是CLR直接优化的特殊类型,普通集合则需要依赖语言特性来达到类似效果。
内容的提问来源于stack exchange,提问作者scharnyw
相关产品推荐
相关产品推荐

