基于模式的 fixed
语句Pattern-based fixed
statement
总结Summary
引入允许类型参与语句的模式 fixed
。Introduce a pattern that would allow types to participate in fixed
statements.
动机Motivation
语言提供了一种机制,用于固定托管数据并获取指向基础缓冲区的本机指针。The language provides a mechanism for pinning managed data and obtain a native pointer to the underlying buffer.
fixed(byte* ptr = byteArray)
{
// ptr is a native pointer to the first element of the array
// byteArray is protected from being moved/collected by the GC for the duration of this block
}
可以参与的类型集 fixed
是硬编码的,并限于数组和 System.String
。The set of types that can participate in fixed
is hardcoded and limited to arrays and System.String
. 如果引入了新的基元(如),则硬编码的 "特殊" 类型不会缩放 ImmutableArray<T>
Span<T>
Utf8String
。Hardcoding "special" types does not scale when new primitives such as ImmutableArray<T>
, Span<T>
, Utf8String
are introduced.
此外,的当前解决方案依赖于 System.String
相当严格的 API。In addition, the current solution for System.String
relies on a fairly rigid API. API 的形状表示, System.String
是一个连续的对象,它将 UTF16 编码数据嵌入对象标头的固定偏移量。The shape of the API implies that System.String
is a contiguous object that embeds UTF16 encoded data at a fixed offset from the object header. 在多个可能需要更改基础布局的建议中发现此类方法存在问题。Such approach has been found problematic in several proposals that could require changes to the underlying layout. 需要能够切换到更灵活的方法,以便在 System.String
非托管互操作目的中分离对象的内部表示形式。It would be desirable to be able to switch to something more flexible that decouples System.String
object from its internal representation for the purpose of unmanaged interop.
详细设计Detailed design
模式Pattern
可行的基于模式的 "固定" 需要:A viable pattern-based “fixed” need to:
- 提供托管引用以固定实例并初始化指针 (最好是相同的引用) Provide the managed references to pin the instance and to initialize the pointer (preferably this is the same reference)
- 明确传达非托管元素的类型 (即 "char" for "string" ) Convey unambiguously the type of the unmanaged element (i.e. “char” for “string”)
- 当没有任何引用时,规定 "empty" 情况下的行为。Prescribe the behavior in "empty" case when there is nothing to refer to.
- 不应将 API 作者推送到影响外部类型使用的设计决策
fixed
。Should not push API authors toward design decisions that hurt the use of the type outside offixed
.
我认为上述内容可以通过识别一个特别命名的引用返回的成员来满足: ref [readonly] T GetPinnableReference()
。I think the above could be satisfied by recognizing a specially named ref-returning member: ref [readonly] T GetPinnableReference()
.
为了使语句使用, fixed
必须满足以下条件:In order to be used by the fixed
statement the following conditions must be met:
- 只为一个类型提供了一个这样的成员。There is only one such member provided for a type.
- 返回
ref
或ref readonly
。Returns byref
orref readonly
.readonly
允许使用 (以便不可变/只读类型的作者可以实现模式,而无需添加可在安全代码中使用的可写 API) (readonly
is permitted so that authors of immutable/readonly types could implement the pattern without adding writeable API that could be used in safe code) - T 是非托管类型。T is an unmanaged type.
由于
T*
变为指针类型,因此 (。(sinceT*
becomes the pointer type. 如果 "非托管" 的概念已展开,则限制将自然展开) The restriction will naturally expand if/when the notion of "unmanaged" is expanded) - 当没有
nullptr
要固定的数据时,将返回 managed –可能是传达空的最便宜的方式。Returns managednullptr
when there is no data to pin – probably the cheapest way to convey emptiness. (请注意,"" 字符串返回对 "\ 0" 的引用,因为字符串以 null 结尾) (note that “” string returns a ref to '\0' since strings are null-terminated)
对于, #3
我们可以允许空事例中的结果为未定义或特定于实现的结果。Alternatively for the #3
we can allow the result in empty cases be undefined or implementation-specific. 但是,这可能会使 API 更具危险性,并且容易受到滥用和意外的兼容性负担。That, however, may make the API more dangerous and prone to abuse and unintended compatibility burdens.
翻译Translation
fixed(byte* ptr = thing)
{
// <BODY>
}
成为以下伪代码, (不是 c # 中的所有可表达 ) becomes the following pseudocode (not all expressible in C#)
byte* ptr;
// specially decorated "pinned" IL local slot, not visible to user code.
pinned ref byte _pinned;
try
{
// NOTE: null check is omitted for value types
// NOTE: `thing` is evaluated only once (temporary is introduced if necessary)
if (thing != null)
{
// obtain and "pin" the reference
_pinned = ref thing.GetPinnableReference();
// unsafe cast in IL
ptr = (byte*)_pinned;
}
else
{
ptr = default(byte*);
}
// <BODY>
}
finally // finally can be omitted when not observable
{
// "unpin" the object
_pinned = nullptr;
}
缺点Drawbacks
- GetPinnableReference 仅适用于
fixed
,但不会阻止它在安全代码中的使用,因此实现器必须记住这一点。GetPinnableReference is intended to be used only infixed
, but nothing prevents its use in safe code, so implementor must keep that in mind.
备选方法Alternatives
用户可以引入 GetPinnableReference 或类似成员,并将其用作Users can introduce GetPinnableReference or similar member and use it as
fixed(byte* ptr = thing.GetPinnableReference())
{
// <BODY>
}
System.String
如果需要备用解决方案,则没有解决方案。There is no solution for System.String
if alternative solution is desired.
未解决的问题Unresolved questions
- [] 在 "empty" 状态中的行为。[ ] Behavior in "empty" state. -
nullptr
还是undefined
? -nullptr
orundefined
? - [] 是否应考虑扩展方法?[ ] Should the extension methods be considered ?
- [] 如果在上检测到模式
System.String
,是否应接管此模式?[ ] If a pattern is detected onSystem.String
, should it win over ?
设计会议Design meetings
尚无。None yet.