Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 对于本身为结构的字段,LayoutKind.explicit的.NET行为 问题:_C#_.net_Struct_Offset_Layoutkind.explicit - Fatal编程技术网

C# 对于本身为结构的字段,LayoutKind.explicit的.NET行为 问题:

C# 对于本身为结构的字段,LayoutKind.explicit的.NET行为 问题:,c#,.net,struct,offset,layoutkind.explicit,C#,.net,Struct,Offset,Layoutkind.explicit,我尝试使用[StructLayout(LayoutKind.Explicit)]构建一个结构(SA),其中有一个字段是另一个结构(SB) 首先:我很惊讶我被允许在没有[StructLayout(LayoutKind.Explicit)]的情况下声明另一个结构,而在SA中,所有字段都必须有[FieldOffset(0)],否则编译器会大喊大叫。这没有多大意义 这是编译器警告/错误中的漏洞吗 Second:似乎SB中的所有引用(object)字段都移动到了SB的前面 这种行为在哪里被描述过 它

我尝试使用
[StructLayout(LayoutKind.Explicit)]
构建一个结构(
SA
),其中有一个字段是另一个
结构(
SB

首先:我很惊讶我被允许在没有
[StructLayout(LayoutKind.Explicit)]
的情况下声明另一个结构,而在
SA
中,所有字段都必须有
[FieldOffset(0)]
,否则编译器会大喊大叫。这没有多大意义

  • 这是编译器警告/错误中的漏洞吗
Second:似乎
SB
中的所有引用(
object
)字段都移动到了
SB
的前面

  • 这种行为在哪里被描述过
  • 它是否取决于实现
  • 它是否定义在依赖于实现的任何地方<代码>:)
注意:我不打算在生产代码中使用此选项。我问这个问题主要是出于好奇

实验
第一个问题的答案是否定的,编译器的错误报告中没有漏洞或bug。如果您开始执行显式布局,编译器将假定您知道自己在做什么(在限制范围内——请参见下文)。你让它把一个结构叠加在另一个上面。编译器不(也不应该)关心您覆盖的结构是否也没有显式布局

如果编译器真的在意,那么您将无法覆盖任何未显式布局的类型,这意味着您无法在一般情况下进行联合。例如,尝试覆盖<代码>日期时间<代码>和<代码>长< /代码>:

[StructLayout(LayoutKind.Explicit)]
struct MyUnion
{
    [FieldOffset(0)]
    public bool IsDate;
    [FieldOffset(1)]
    public DateTime dt;
    [FieldOffset(1)]
    public long counter;
}
除非明确列出
DateTime
,否则无法编译。可能不是你想要的

至于将引用类型放入显式布局的结构中,您的结果将是。。。可能不是你所期望的。例如,考虑这个简单的比特:

struct MyUnion
{
    [FieldOffset(0)]
    public object o1;
    [FieldOffset(0)]
    public SomeRefType o2;
}
这在很大程度上违反了类型安全性。如果它进行编译(很可能),当您尝试使用它时,它将因TypeLoadException而消亡

编译器将尽可能防止您违反类型安全性。我不知道编译器是否知道如何处理这些属性和布局结构,或者它是否只是通过生成的MSIL将布局信息传递给运行时。考虑到您的第二个示例,可能是后者,其中编译器允许特定的布局,但运行时遇到了TypeLoadException

谷歌在[structlayout.explicit reference types]上的搜索揭示了一些有趣的讨论。例如,见

这是编译器警告/错误中的漏洞吗

不,没问题。字段允许重叠,这就是为什么LayoutKind.Explicit首先存在的原因。它允许在非托管代码中声明联合的等价物,而C#不支持这种声明。不能突然停止在结构声明中使用[FieldOffset],运行时会坚持在结构的所有成员上使用它。这在技术上不是必需的,只是一个避免错误假设的简单要求

似乎SB中的所有引用(对象)字段都已移动


是的,这很正常。CLR以未记录和不可发现的方式布置对象。它使用的确切规则没有文档记录,可能会发生更改。对于不同的抖动,它也不会重复。直到对象被封送、Marshal.StructureToPtr()调用或被pinvoke封送器隐式封送,布局才变得可预测。这是唯一一次确切的布局问题。我在中写到了这种行为的基本原理。

MyUnion结构没有TypeLoadException。只有当您将值类型值与引用类型对象重叠时,它才会崩溃。就像那次行动一样。试试看,似乎。。。危险的或许不是。嗯。我想覆盖两个引用类型是安全的。如果在将
o1
设置为非
SomeRefType
的对象后尝试取消对
o2
的引用,则会发生运行时异常。这对我来说似乎很危险:可以使用不同的类型覆盖两个数组。这允许您访问较小数组区域以外的内存。@Ark kun:运行时将防止引用类型与值类型重叠。如果覆盖两种值类型,则分配的内存量将是两种值类型中较大的一种。因此,您提到的场景不是问题。@JimMischel覆盖两个数组并不是“用值类型覆盖引用类型”。数组是.Net中的引用。重叠数组允许绕过CLR类型检查和数组边界保护以及损坏的内存。如果我没有弄错的话,当我们将原始不安全指针转换为指向该结构的指针时,该结构也具有“类似封送的布局”,不是吗?比如说,在
不安全的{SA*SA=(SA*)0x1234;/*在这里使用SA*/}
中,
SA
将具有与封送时相同的布局,不是吗?不,这不是封送。编译器不允许您这样做,结构是不可blittable的。确实,它只适用于包含int的结构,但不适用于添加对象字段的结构。然而,对于只包含t
int
s之类的结构,这个问题仍然存在,就像我使用的第一个结构一样。
struct MyUnion
{
    [FieldOffset(0)]
    public object o1;
    [FieldOffset(0)]
    public SomeRefType o2;
}