如果结构体可以实现IDisposable接口,为什么它们不能有析构函数?

5

我阅读了一个类似问题的答案,其中一部分回答如下:

当结构体作为参数传递时,它们会按值传递:它们被复制。现在你有两个具有相同内部字段的结构体,它们都将尝试清理相同的对象。其中一个将首先发生,因此在之后使用另一个的代码将开始出现神秘故障...然后它自己的清理也将失败

这个问题不也适用于Dispose()吗?如果结构体可以实现IDisposable,那么为什么不允许它们有终结器呢?

如果终结器的整个目的是在程序员忘记调用Dispose()的情况下调用Dispose(false),并且结构体可以有IDisposable.Dispose(),那么为什么不允许结构体有终结器,但允许引用类型有终结器呢?


2
IDisposable只是一个普通的接口。结构体可以实现接口,因此它们也可以实现IDisposable。 - Evk
1
一个结构体不能有析构函数。析构函数只是对象.Finalize的伪装,而结构体作为值类型,不受垃圾回收的影响。 - Nisarg Shah
@Igor,原帖提到了那个问题,并提出了一个相关的问题。不是重复。 - Luaan
@Evk 我知道结构体可以实现接口,但我更感兴趣的是为什么不允许它们拥有 finalizers,如果它们被允许实现 IDisposable。 - David Klempfner
为什么它们不能拥有终结器在你提供的答案中有解释。IDisposable与终结器没有任何关系,为什么允许使用它在下面的答案中有解释。 - Evk
显示剩余2条评论
2个回答

2
因为IDisposable只是一个接口,没有特殊处理。结构体可以实现接口,因此它们可以实现IDisposable
然而,这并不意味着它没有意义。 IDisposable的目的是释放非托管资源。结构体可以有对非托管资源的引用,并且会从Dispose中受益(不用说,该引用本身应该实现IDisposable并具有终结器)。
作为奖励,Dispose通常作为using模式的一部分使用。您仅为使用块创建实例,保留引用直到Dispose,没有任何奇怪之处。确实没有理由禁止这样做。

1
但是,如果一个结构体拥有对非托管资源的引用,那么OP的担忧是合理的:释放一个副本的结构体也会打破所有其他副本。 - user743382
@hvd 取决于你所谓的“break”。但无论如何,只要您不修改任何本地字段(您不应该这样做),它就不会与具有多个引用的类有所不同。 - Luaan
默认情况下,类无法被复制,因此从这个意义上说,它的行为是不同的:创建副本的能力为程序员提供了额外的机会来出错。但我理解你的观点。 - user743382
@hvd 批准,但只复制对非托管对象的引用 - 它仍然只有一个实例。不过,应该毫无疑问地指出,您永远不应该拥有对非托管对象的非托管引用(例如,包装IntPtr的结构体只会带来麻烦)。 - Luaan

2
这个问题是否也适用于Dispose()
有点类似,但并非完全相同。具体来说,“然后它自己的清理将失败”是可能的,但不太可能发生,因为Dispose()必须安全地在同一对象上多次调用,并且通常在不同副本上多次调用它不会成为问题。
如果结构体不能有终结器,为什么允许它们实现IDisposable
允许这样做是自然而然的行为;它给语言提供了更简单的规则。由于这不是程序员意外犯错的事情,因此编写额外的编译器代码以拒绝此操作的好处很小。
Jeroen Mostert还指出,即使对于结构体实现IDisposable也可能非常有意义: IDisposable可能只是因为其他某些代码的要求而被实现,即使在该特定类型上实现Dispose()将绝对不起作用。在这种情况下,在不同的副本上意外调用它没有风险。一个例子是当结构体实现IEnumerator<T>时,其中IEnumerator<T>又实现了IDisposable

2
实际上,允许它有一个相当好的用例,而不仅仅是不禁止它:IEnumerator 实现了 IDisposable。微不足道的枚举器没有可处理的资源和几乎没有状态,因此将它们实现为结构体是有意义的,并且实际上在框架本身中使用。(例如:Dictionary<TKey, TValue>+Enumerator。) - Jeroen Mostert
@JeroenMostert 你指的是 IEnumerator<T>,而不是 IEnumerator,但是非常好的观点。我会提到它的。 - user743382
哦,我错过了那个。实际上这会让问题变得有点混乱,因为通过检查实例是否实现接口来处理 IEnumerator 仍然是可以的。由于某种原因,在泛型变量中,他们决定将其嵌入到接口中,但这并不是必须要做的。(当然,最初实现枚举器时有很多奇怪的地方。) - Jeroen Mostert
@JeroenMostert,IEnumerator也应该这样做,但由于它已经在没有IDisposable的情况下发布了,稍后修复这个疏忽会破坏太多现有的代码。https://dev59.com/YnVC5IYBdhLWcg3woCrN - user743382

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接