【问题标题】:Is it possible to create a non-boxable struct in C#是否可以在 C# 中创建不可装箱的结构
【发布时间】:2014-07-13 08:22:43
【问题描述】:

某些数据的副本不属于堆(没有特殊处理)。例如,堆栈上的JavaScriptValue 结构在与 Chakra JavaScript 引擎的互操作方面做得很好。

但据我了解,将这样的值存储在托管堆上(没有正确的引用计数)会导致可能的悬空引用。有没有办法将这样的 c# 结构声明为不可装箱

澄清,如果对类型生成任何装箱操作,我希望 c# 编译器生成错误消息。

参考:见MSDN JSAddRef reference 上面写着:

这只需要在 不会被存储的 JsRef 句柄上调用 堆栈中的某处。 调用 JsAddRef 可确保 JsRef 引用的对象 > 在调用 JsRelease 之前不会释放 to。

据我推测,Chakra 可能正在使用“保守的垃圾回收”方法,使得基于堆栈的 JsRef 句柄不需要 JsAddRef/JsRelease。 我想使用一个类(而不是结构) 将 JsRef 句柄包装在堆上,并进行转换操作调用 JsAddRef,并使用 IDisposable 模式调用 JsRelease。

【问题讨论】:

  • 它可以像局部变量一样轻松悬挂。 all 使用中需要正确的引用计数。

标签: c# clr boxing chakra


【解决方案1】:

对于 C# 7.2+,答案是肯定的:只需在结构 ref struct 上使用 ref 关键字。它只允许在堆栈上,而不是在堆上(因此不能对其进行装箱)。

更多信息请看这里:http://www.devsanon.com/c/c-7-2-introducing-ref-struct/

这里: https://docs.microsoft.com/en-us/dotnet/csharp/write-safe-efficient-code#use-ref-struct-types

【讨论】:

    【解决方案2】:

    CLI ECMA-335 specification 说(第 I.8.2.4 节):

    所有值类型都有一个名为box 的操作。对任何值类型的值进行装箱会产生其装箱值;即,对应的装箱类型的值包含 原值。

    还有一些额外的语言说明Nullable<T> 会发生什么(包含的值被装箱或产生空引用)并且无法对托管指针执行装箱操作,但没有提及任何值异常类型。

    【讨论】:

    • 有人在没有评论原因的情况下否决了这个答案。基本上我相信我最初的问题的答案是不,它不能完成,这个答案提供了权威的理由,所以我接受它。
    【解决方案3】:

    它被装箱了,因为您将它存储在堆上的数据结构中。

    把它放在一个非值类型中。

    围绕这个主题有很多问题。如果你不想读一本书,那么你应该阅读所有这些。其中一些很有趣。

    所以不,它不可能创建一个不可装箱的结构 - 因为你所做的事情,它会被装箱。

    【讨论】:

    • 您能详细说明“包装”吗?我知道任何由引用类型组成的值类型都会存储在堆上,但是在这种情况下会发生装箱操作吗?
    • 谢谢约翰,你可能是对的,但有点没抓住重点。我想声明一个如果装箱会产生错误的结构。我会在上面澄清我的问题。
    • 好的,我不知道。但我不认为你有那么多的控制权。我假设没有,但这更像是我不知道而不是绝对没有。但是如果有效地提供了 .net 的源代码,您现在可以做任何您喜欢的事情。问题是您无法覆盖 box 方法 - 无论如何您都无法直接访问它。您基本上是在尝试定义语言的定义 - 以及它如何管理内存
    • @Burt_Harris github.com/controlflow/resharper-heapview 这个 resharper 扩展将让您获得想要的东西很长的路要走
    【解决方案4】:

    正如其他人所写的那样,值类型将被装箱(您可以将其想象为它们被包装在引用类型中)以便能够在堆上进行。

    但更重要的是,这与悬空引用和引用计数无关。不管它存储在哪里。

    我相信您要问的是如何管理原生资源,这是另一回事。如果您从 .NET 世界之外的资源分配资源,通常需要确保在完成后释放/释放它。通常,您将其包装在实现IDisposable 的类中并手动(或在析构函数中)处理它。现在,如果你忘记处理你应该处理的东西,或者保留对已经处理的东西的引用,你可能会遇到麻烦。但这一切都取决于资源是什么。

    对于您使用JavaScriptValue 的具体情况,我真的不知道它是如何工作的。尽管在我看来,如果那里的 IntPtr 提到了需要处理的东西,JavaScriptValue 将是一个实现 IDisposable 的类而不是一个结构。将需要处理的内容存储在结构中将是陷入麻烦的一种很好的方法,因为您可能会在整个地方拥有该引用的副本。所以我会假设你不需要打扰 - 但你应该检查文档以确定。

    【讨论】:

    • 谢谢克里斯,这似乎是迄今为止最好的答案,但我不同意,这与悬空引用和引用计数有关。我已经对问题进行了一些澄清。当存储在堆栈上时,本机 JsRef 句柄不需要额外的 AddRef,但如果存储在堆上,那么它就需要(使得 IDisposable 仅适用于基于堆的实例。)
    • 嗯,是的,如果设计师做出了奇怪的选择,将结构用于类似的事情,我完全同意。
    猜你喜欢
    • 1970-01-01
    • 2018-07-23
    • 2014-03-30
    • 2017-08-04
    • 1970-01-01
    • 1970-01-01
    • 2016-05-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多