为什么结构体不能有析构函数?
您认为面试中对此类问题的最佳答案是什么?
我想我在这里没有找到该副本,如果有请链接。
What is best answer on interview on such question you think?
I think I didn't find a copy of this here, if there is one please link it.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
来自 Jon Jagger:
“结构不能有析构函数。析构函数只是变相覆盖
object.Finalize
,而结构作为值类型,不受垃圾回收的影响。”From Jon Jagger:
"A struct cannot have a destructor. A destructor is just an override of
object.Finalize
in disguise, and structs, being value types, are not subject to garbage collection."除数组和字符串之外的每个对象都以相同的方式存储在堆上:一个标头,提供有关“对象相关”属性的信息(其类型、是否由任何活动监视器锁使用、是否具有非抑制)
Finalize
方法等)及其数据(意味着所有类型的实例字段的内容(公共、私有和受保护的混合字段,基类字段出现在派生类型字段之前)。因为每一个堆对象有一个标头,系统可以引用任何对象并知道它是什么,以及垃圾收集器应该用它做什么如果系统有一个已创建的所有对象的列表,并且有一个标头。Finalize
方法,它可以检查列表中的每个对象,查看其Finalize
方法是否被取消抑制,并对其进行适当的操作,而结构体则存储在没有任何标头的情况下;具有两个整数字段的 Point 简单地存储为两个整数,虽然可以有一个结构的引用(当结构作为结构传递时会创建这样的东西)。
ref
参数),使用ref
的代码必须知道ref
指向什么类型的结构,因为ref 都不知道
结构本身也不保存该信息。此外,堆对象只能由垃圾收集器创建,这将保证创建的任何对象将始终存在,直到下一个 GC 周期。相比之下,用户代码可以自行创建和销毁结构(通常在堆栈上);如果代码创建一个结构体以及一个 ref 到它,并将该 ref 传递给一个被调用的例程,那么代码就不可能销毁该结构体(或者在所有,就此而言)直到被调用的例程返回,因此保证该结构至少存在直到被调用的例程退出。另一方面,一旦被调用的例程退出,它所给出的 ref 就应该被假定为无效,因为调用者可以在此后的任何时间自由地销毁该结构。Every object other than arrays and strings is stored on the heap in the same way: a header which gives information about the "object-related" properties (its type, whether it's used by any active monitor locks, whether it has a non-suppressed
Finalize
method, etc.), and its data (meaning the contents of all the type's instance fields (public, private, and protected intermixed, with base-class fields appearing before derived-type fields). Because every heap object has a header, the system can take a reference to any object and know what it is, and what the garbage-collector is supposed to do with it. If the system has a list of all objects which have been created and have aFinalize
method, it can examine every object in the list, see if itsFinalize
method is unsuppressed, and act on it appropriately.Structs are stored without any header; a struct like
Point
with two integer fields is simply stored as two integers. While it is possible to have aref
to a struct (such a thing is created when a struct is passed as aref
parameter), the code that uses theref
has to know what type of struct theref
points to, since neither theref
nor the struct itself holds that information. Further, heap objects may only be created by the garbage-collector, which will guarantee that any object which is created will always exist until the next GC cycle. By contrast, user code can create and destroy structs by itself (often on the stack); if code creates a struct along with aref
to it, and passes thatref
it to a called routine, there's no way that code can destroy the struct (or do anything at all, for that matter) until the called routine returns, so the struct is guaranteed to exist at least until the called routine exits. On the other hand, once the called routine exits, theref
it was given should be presumed invalid, since the caller would be free to destroy the struct at any time thereafter.因为根据定义,析构函数用于析构类的实例,而结构是值类型。
参考:http://msdn.microsoft.com/en-us/library/66x5fx1b.aspx
用微软自己的话说:
“析构函数用于析构类的实例。”
所以问“为什么不能在(不是类的东西)上使用析构函数有点愚蠢?” ^^Becuase by definition destructors are used to destruct instances of classes, and structs are value types.
Ref: http://msdn.microsoft.com/en-us/library/66x5fx1b.aspx
By Microsoft's own words:
"Destructors are used to destruct instances of classes."
so it's a little silly to ask "Why can't you use a destructor on (something that is not a class)?" ^^另一种看待这个问题的方式 - 而不是仅仅引用说明结构不能/没有析构函数的规范 - 考虑如果规范被更改以便他们这样做会发生什么 - 或者更确切地说,让我们问一个问题:可以我们猜想为什么语言设计者首先决定不允许结构具有“析构函数”?
(不要在这里纠结于“析构函数”这个词;我们基本上讨论的是结构体上的一种神奇方法,当变量超出范围时,该方法会自动调用。换句话说,这是一种类似于 C++ 析构函数的语言功能。 )
首先要认识到的是我们并不关心释放内存。无论对象位于堆栈上还是堆上(例如类中的结构),迟早都会以一种或另一种方式处理内存;要么从堆栈中弹出,要么被收集。首先拥有类似析构函数的东西的真正原因是为了管理外部资源 - 例如文件句柄、窗口句柄或其他需要特殊处理才能清理 CLR 的东西自己也不知道。
现在假设您允许一个结构体有一个可以执行此清理操作的析构函数。美好的。直到您意识到当结构作为参数传递时,它们是按值传递的:它们被复制。现在您有两个具有相同内部字段的结构,并且它们都将尝试清理同一个对象。一个会首先发生,因此随后使用另一个的代码将开始神秘地失败......然后它自己的清理将失败(希望如此! - 最坏的情况是它可能成功清理一些其他随机资源 - 这可以例如,在重用句柄值的情况下会发生这种情况。)
您可以想象为作为参数的结构创建一个特殊情况,以便它们的“析构函数”不会运行(但要小心 - 现在您需要记住,在调用函数时,它始终是“拥有”实际资源的外部结构 - 所以现在有些结构与其他结构略有不同......) - 但是常规结构变量仍然存在这个问题,其中一个可以分配给另一个,从而制作副本。
您也许可以通过向分配操作添加特殊机制来解决此问题,该机制以某种方式允许新结构与其新副本协商底层资源的所有权 - 也许它们共享它或将所有权直接从旧结构转移到新结构 - 但现在你本质上已经进入了 C++ 领域,在那里你需要复制构造函数、赋值运算符,并且添加了一堆微妙之处等待着陷阱不知情的新手程序员。请记住,C# 的全部要点是尽可能避免这种 C++ 式的复杂性。
而且,只是为了让事情变得更加混乱,正如其他答案之一指出的那样,结构不仅仅作为本地对象存在。对于当地人来说,范围很好并且定义明确;但结构也可以是类对象的成员。在这种情况下,什么时候应该调用“析构函数”?当然,当容器类最终确定时就可以这样做;但现在你有一个机制,根据结构所在的位置,其行为非常不同:如果结构是本地结构,它会在作用域结束时立即触发;如果该结构位于一个类中,它会被延迟触发...因此,如果您确实关心确保某个结构中的某些资源在某个时间被清理,并且您的结构最终可能成为某个结构的成员,类,你可能需要一些明确的东西,比如 IDisposable/using() 无论如何,以确保你已经覆盖了你的基础。
因此,虽然我不能声称代表语言设计者,但我可以很好地猜测他们决定不包含此类功能的一个原因是因为这会是一堆蠕虫,而且他们希望保持 C# 相当简单。
Another way of looking at this - rather than just quoting the spec which says that structs can't/don't have destructors - consider what would happen if the spec was changed so that they did - or rather, let's ask the question: can we guess why did the language designers decide to not allow structs to have 'destructors' in the first place?
(Don't get hung up on the word 'destructor' here; we're basically talking about a magic method on structs that gets called automatically when the variable goes out of scope. In other words, a language feature analogous to C++'s destructors.)
The first thing to realize is that we don't care about releasing memory. Whether the object is on the stack or on the heap (eg. a struct in a class), the memory will be taken care of one way or another sooner or later; either by being popped off the stack or by being collected. The real reason for having something that's destructor-like in the first place is for managing external resources - things like file handles, window handles, or other things that need special handling to get them cleaned up that the CLR itself doesn't know about.
Now supposed you allow a struct to have a destructor that can do this cleanup. Fine. Until you realize that when structs are passed as parameters, they get passed by value: they are copied. Now you've got two structs with the same internal fields, and they're both going to attempt to clean up the same object. One will happen first, and so code that is using the other one afterwards will start to fail mysteriously... and then its own cleanup will fail (hopefully! - worst case is it might succeed in cleaning up some other random resource - this can happen in situations where handle values are reused, for example.)
You could conceivably make a special case for structs that are parameters so that their 'destructors' don't run (but be careful - you now need to remember that when calling a function, it's always the outer one that 'owns' the actual resource - so now some structs are subtly different to others...) - but then you still have this problem with regular struct variables, where one can be assigned to another, making a copy.
You could perhaps work around this by adding a special mechanism to assignment operations that somehow allows the new struct to negotiate ownership of the underlying resource with its new copy - perhaps they share it or transfer ownership outright from the old to the new - but now you've essentially headed off into C++-land, where you need copy constructors, assignment operators, and have added a bunch of subtleties waiting to trap the unaware novice programmer. And keep in mind that the entire point of C# is to avoid that type of C++-style complexity as much as possible.
And, just to make things a bit more confusing, as one of the other answers pointed out, structs don't just exist as local objects. With locals, scope is nice and well defined; but structs can also be members of a class object. When should the 'destructor' get called in that case? Sure, you can do it when the container class is finalized; but now you have a mechanism that behaves very differently depending on where the struct lives: if the struct is a local, it gets triggered immediately at end of scope; if the struct is within a class, it gets triggered lazily... So if you really care about ensuring that some resource in one of your structs is cleaned up at a certain time, and if your struct could end up as a member of a class, you'd probably need something explicit like IDisposable/using() anyhow to ensure you've got your bases covered.
So while I can't claim to speak for the language designers, I can make a pretty good guess that one reason they decided not to include such a feature is because it would be a can of worms, and they wanted to keep C# reasonably simple.