其实这个 ref 就是 C/C++ 中的指针一样。
16. 请利用 foreach 和 ref 为一个数组中的每个元素加 1
int[] arr = { 1, 2, 3, 4, 5};
Console.WriteLine(string.Join(",", arr)); // 1,2,3,4,5
foreach (ref int v in arr.AsSpan())
{
v++;
}
Console.WriteLine(string.Join(",", arr)); // 2,3,4,5,6
注意 foreach 不能用 var ,也不能直接用 int ,需要 ref int ,注意 arr 要转换为 Span<T> 。
17. 请简述 ref 、 out 和 in 在用作函数参数修饰符时的区别
ref 参数可同时用于输入或输出(变量使用前必须初始化);
out 参数只用于输出(使用前无需初始化);
in 参数只用于输入,它按引用传递,它能确保在使用过程中不被修改(变量使用前必须初始化);
可以用一个表格来比较它们的区别:
| 修饰符/区别 | ref | out | in | 无 |
| 是否复制 | × | × | × | √ |
| 能修改 | √ | √ | × | × |
| 输入 | √ | × | √ | √ |
| 输出 | √ | √ | × | × |
| 需初始化 | √ | × | √ | √ |
其实in就相当于C++中的const T&,我多年前就希望C#加入这个功能了。
18. 请简述非 sealed 类的 IDisposable 实现方法
正常IDisposable实现只有一个方法即可:
void Dispose()
{
// free managed resources...
// free unmanaged resources...
}
但它的缺点是必须手动调用Dispose()或使用using方法,如果忘记调用了,系统的垃圾回收器不会清理,这样就会存在资源浪费,如果调用多次,可能会存在问题,因此需要Dispose模式。
Dispose模式需要关心C#的终结器函数(有人称为析构函数,但我不推荐叫这个名字,因为它并不和constructor构造函数对应),其最终版应该如下所示:
class BaseClass : IDisposable
{
private bool disposed = false;
~BaseClass()
{
Dispose(disposing: false);
}
protected virtual void Dispose(bool disposing)
{
if (disposed) return;
if (disposing)
{
// free managed resources...
}
// free unmanaged resources...
disposed = true;
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
它有如下要注意的点:
(1)引入disposed变量用于判断是否已经回收过,如果回收过则不再回收;
(2)使用protected virtual来确保子类的正确回收,注意不是在Dispose方法上加;










