`Marshal.SizeOf<T>()` 和 `Marshal.SizeOf<T>(T 结构)` 的行为不一致

问题描述 投票:0回答:1

我看到

Marshal.SizeOf<>
方法 重载的行为不一致。为了简洁起见,我在这里仅考虑通用重载。

已在 .NET 8 运行时尝试过此操作。

问题1:

Marshal.SizeOf<T>()
允许太少

考虑这段代码:

using System.Collections.Generic;
using System.Runtime.InteropServices;

...

    int size1a = Marshal.SizeOf<KeyValuePair<int, int>>(KeyValuePair.Create(123, 456));  // good, gives 8
    int size1b = Marshal.SizeOf<KeyValuePair<int, int>>(default);                        // good, gives 8
    int size2 = Marshal.SizeOf<KeyValuePair<int, int>>();                                // throws?!?

解释:

KeyValuePair<TKey, TValue>
是一个
struct
,它分别包含两个类型为
TKey
TValue
的非静态字段。当我在
int
struct
两个位置上使用
TKey
TValue
,没有托管字段)构造它时,我有一个类型
KeyValuePair<int, int>
,它不包含引用类型成员,因此是“非托管”。

预期:

size2
可以被确定(如
8
),就像
size1a
size1b

一样

实际:用

ArgumentException
爆炸,说
T
不能是泛型类型

问题:当使用arg的重载没有问题时发现大小没有问题,这不是完全不一致吗

structure

请注意,封闭类型

KeyValuePair<int, int>
将很高兴满足自 C# 7.3(从 2018 年起)以来存在的通用约束
where TSomething : unmanaged
。这也使得我们很自然地期望它的大小是可以确定的。

问题2:

Marshal.SizeOf<T>(T structure)
允许太多!

让我们在

KeyValuePair<,>
中放入托管类型:

using System.Collections.Generic;
using System.Runtime.InteropServices;

...

    int size3 = Marshal.SizeOf<KeyValuePair<string, string>>(KeyValuePair.Create("arbitrarily long text", "can go in here"));

预期:调用应导致异常,因为类型/实例保存对托管对象的引用(在此示例中为类型

System.String
(类))。

实际:调用成功,返回值取决于构建平台。例如,当为

x86
构建时,
size3
变为
8
(== 4+4),为
x64
构建时,
16
(== 8+8),依此类推。

问题:为什么?我看到运行时引用的宽度是两倍。

这在 .NET Framework 4.8 上可以正常工作。在这里,尝试找到

size3
(必须显式使用
new KeyValuePair<string, string>(...)
,因为静态
Create
方法不存在)会导致:

ArgumentException:类型“System.Collections.Generic.KeyValuePair`2[System.String,System.String]”无法封送为非托管结构;无法计算出有意义的大小或偏移量。

因此,问题 2 在 .NET Core 上一定是新的。正如我所说,我正在使用 .NET 8。


我应该将此报告为 .NET 中的错误,还是我忽略了某些内容?

c# .net-core size marshalling sizeof
1个回答
0
投票

替代方案:

对于问题 1:处理非托管类型时,可以在调用 Marshal.SizeOf 时显式指定类型。例如,您可以执行以下操作,而不是 Marshal.SizeOf>():

int size2 = Marshal.SizeOf(typeof(KeyValuePair<int, int>));

通过这种方式,您可以显式提供类型信息,并且它应该可以正常工作而不会引发异常。

对于问题 2:当需要计算大小时,可以通过不将 KeyValuePair 与引用类型一起使用来避免该问题。相反,您可以创建一个自定义结构来表示您要使用的数据,确保它仅包含值类型(而不是引用类型)。例如:

public struct MyKeyValuePair
{
    public string Key;
    public string Value;
}

然后,您可以计算此自定义结构体的大小,而不会遇到与引用类型相关的问题:

int size3 = Marshal.SizeOf(typeof(MyKeyValuePair));
By using a custom struct that only contains value types, you avoid the problem of Marshal.SizeOf incorrectly calculating the size based on reference types.

请记住,这些解决方法可能需要重构您的代码,尤其是在问题 2 的情况下,您需要将 KeyValuePair 的用法替换为自定义结构。这些解决方法针对您所描述的特定场景解决了 .NET Core/.NET 5+ 中 Marshal.SizeOf 方法的限制。

© www.soinside.com 2019 - 2024. All rights reserved.