为什么结构不能有析构函数?

时间:2011-11-26 03:25:10

标签: c# .net struct destructor finalizer

你认为这个问题的采访最佳答案是什么?

我想我在这里找不到这个副本,如果有的话请链接它。

4 个答案:

答案 0 :(得分:50)

另一种看待这种情况的方式 - 而不是仅仅引用说明结构不能/不具有析构函数的规范 - 考虑如果规范被改变以便他们这样做会发生什么 - 或者更确切地说,让我们问一下问题:我们可以猜测为什么语言设计师决定不让结构首先出现“析构函数”?

(不要挂在这里的“析构函数”这个词;我们基本上是在谈论结构上的一种神奇的方法,当变量超出范围时会自动调用。换句话说,语言特征类似于C ++的析构函数。)

要意识到的第一件事是我们不关心释放记忆。无论对象是在堆栈上还是在堆上(例如,类中的结构),存储器迟早会以这种或那种方式处理;通过弹出堆栈或收集。首先拥有类似析构函数的东西的真正原因是管理外部资源 - 诸如文件句柄,窗口句柄或其他需要特殊处理以使其清理掉CLR的东西本身不知道。

现在假设您允许结构体具有可以执行此清理的析构函数。精细。直到你意识到结构作为参数传递时,它们才会被值传递:它们被复制。现在你有了两个带有相同内部字段的结构,并且它们将尝试清理同一个对象。一个将首先发生,因此之后使用另一个的代码将开始神秘地失败...然后它自己的清理将失败(希望! - 最坏的情况是它可能成功清理一些其他随机资源 - 这可以例如,在重复使用句柄值的情况下会发生。)

你可以设想一个特殊的情况,对于作为参数的结构,以便它们的“析构函数”不会运行(但要小心 - 你现在需要记住,当调用一个函数时,它始终是'拥有'的外部函数实际的资源 - 所以现在一些结构与其他结构略有不同......) - 但是你仍然遇到常规结构变量的问题,其中一个可以分配给另一个,制作副本。

您可以通过向分配操作添加一种特殊机制来解决这个问题,该机制以某种方式允许新结构与其新副本协商底层资源的所有权 - 也许它们共享它或将所有权从旧的转移到新的 - 但是现在你已经基本上进入了C ++ - land,你需要复制构造函数,赋值运算符,并添加了一堆细微之处,等待陷阱不知不觉的新手程序员。请记住,C#的全部意义在于尽可能避免这种类型的C ++风格。

而且,正如其他答案所指出的那样,只是为了让事情变得更加混乱,结构不仅仅作为本地对象存在。对于本地人来说,范围很好并且定义明确;但结构也可以是类对象的成员。在这种情况下应该何时调用“析构函数”?当然,你可以在容器类完成时做到这一点;但是现在你有一种机制,根据结构所处的位置,行为会有很大不同:如果结构是本地的,它会在范围结束时立即触发;如果struct在一个类中,它会被懒惰地触发......所以如果你真的关心确保你的某个结构中的某些资源在某个时间被清除,并且你的结构可能最终成为一个成员上课时,你可能需要像IDisposable / using()这样明确的东西,以确保你的基础得到覆盖。

因此,虽然我不能声称为语言设计师说话,但我可以很好地猜测他们决定不包含这样一个功能的一个原因是因为它会是一堆蠕虫,并且他们想保留C#相当简单。

答案 1 :(得分:31)

来自Jon Jagger

“结构体不能有析构函数。析构函数只是伪装成object.Finalize的覆盖,而作为值类型的结构体不受垃圾收集的影响。”

答案 2 :(得分:0)

除了数组和字符串之外的每个对象都以相同的方式存储在堆上:一个标题,提供有关“对象相关”属性的信息(其类型,是否由任何活动的监视器锁使用,是否具有非压缩Finalize方法等)及其数据(表示所有类型的实例字段的内容(公共,私有和受保护的混合,在派生类型字段之前出现基类字段)。每个堆对象都有一个头,系统可以引用任何对象并知道它是什么,以及垃圾收集器应该用它做什么。如果系统有一个已经创建并拥有的所有对象的列表一个Finalize方法,它可以检查列表中的每个对象,看看它的Finalize方法是否被解压缩,并对其进行适当的处​​理。

存储结构没有任何标题;像Point这样具有两个整数字段的结构只是存储为两个整数。虽然可以对结构体ref(当结构作为ref参数传递时创建这样的东西),但使用ref的代码必须知道什么ref指向的结构类型,因为ref和结构本身都不包含该信息。此外,堆对象可能只能由垃圾收集器创建,这将保证创建的任何对象将始终存在,直到下一个GC循环。相比之下,用户代码可以自己创建和销毁结构(通常在堆栈上);如果代码与ref一起创建了一个结构,并将ref传递给一个被调用的例程,那么代码就无法破坏结构(或根本不做任何事情)直到被调用的例程返回,所以结构保证至少存在,直到被调用的例程退出。另一方面,一旦被调用的例程退出,它被赋予的ref应该被假定为无效,因为调用者可以在此后的任何时间自由地销毁该结构。

答案 3 :(得分:-4)

因为定义析构函数用于破坏类的实例,而结构体是值类型。

参考:http://msdn.microsoft.com/en-us/library/66x5fx1b.aspx

用微软自己的话来说:"Destructors are used to destruct instances of classes."所以问“为什么你不能使用析构函数(不是类的东西)?”这有点傻了? ^^