解答“60k”大佬的19道C#面试题(下)

2020-06-11 12:00:27王冬梅

其实这个 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方法上加;