C# 检查对象是否为 Span<T>

C# Checking if object is Span<T>

目前我正在 .NET Core 3.1 下编写 C# 代码以检查当前类型(我使用 Mono.Cecil,但 System.Reflection 可能更适合)是否为 Span 类型。找到有关如何检查类型是否为 generic 的信息后,我为此类检查编写了一些伪代码:

        unsafe
        {
            IntPtr unmanagedHandle = Marshal.AllocHGlobal(16);
            Span<byte> unmanaged = new Span<byte>(unmanagedHandle.ToPointer(), 16);
            if (unmanaged is object)
                if ((unmanaged as object).GetType().GetGenericTypeDefinition() == typeof(Span<>))
                    Console.WriteLine("Span!");
            Marshal.FreeHGlobal(unmanagedHandle);
        }

尽管官方 MSDN 文档说 Span<> IS an object,但在编译时,我遇到了警告和错误:CS0184 警告 "unmanaged" is never of the "object" 类型,而 CS0039 告诉我"System.Span<byte>" cannot be converted to "object" via a reference conversion, boxing conversion, unboxing conversion, wrapping conversion, or null type conversion。至少,文档指出 Span<> 确实不是通常的 object

是否有任何其他方法(只有一个依赖项 Mono.Cecil)来确定某些 object(或另一条基础数据,由 IntPtr 引用,至少) 是 Span 派生的?

Span<T> 是一个 ref (stack-only) 结构,这意味着它不能保存到 object 类型的变量中,因为它不能被装箱.

来自 Span<T> docs(强调我的):

Span is a ref struct that is allocated on the stack rather than on the managed heap. Ref struct types have a number of restrictions to ensure that they cannot be promoted to the managed heap, including that they can't be boxed, they can't be assigned to variables of type Object [...]

所以当你有一个变量或参数 object obj 时,你知道 obj 永远不会是 Span<T>,因为 obj 总是指向一个对象堆(例如,当您将整数存储在 obj 中时,它将自动装箱到堆对象中)。

但是它可能是一个Memory<T>,它是Span<T>.[=23=的非堆栈(普通结构)等价物]

好吧,我知道这实际上不太好,但我必须处理名称检查方法。由于 System.Span<T> class 的半对象(无论什么)性质,我们不能将 Span 与任何对象进行比较,但我们可以使用 typeof(System.Span<byte>) 这样的比较。这只有在我们总是知道初步的情况下才有用,如何(即,用哪个 TSpan 可以实例化,但这实际上不是我的情况。除了名称检查之外,似乎没有可靠的机制来区分 Span。因此,检查类型名称似乎是唯一合适的方法。

使用 Mono.Cecil,我使用 TypeReference 以下条件:

bool IsSpanType(TypeReference type) {
    return type.FullName.StartsWith("System.Span`1");
}

如您所见,没有幸福的结局,但它工作得很好。而且我看不出有任何理由再执行任何偏执检查(例如检查 TypeReference 的此类属性,如 ScopeElementType 或其他)。