首页 > 解决方案 > 转换通用 Span到特定的实例化(例如 Span) 如果它在运行时实际上是那种类型

问题描述

考虑以下 C# 代码:

using System;

public static class C
{
    public static int[] TryGetIntArray<T>(T[] x)
    {
        if (x is int[] arr) // ok
            return arr;

        return Array.Empty<int>();
    }

    public static Span<int> TryGetIntSpan<T>(Span<T> x)
    {
        if (x is Span<int> span) // An expression of type 'Span<T>' cannot be handled by a pattern of type 'Span<int>'.
            return span;

        return Span<int>.Empty;
    }
}

这个想法是如果参数在运行时实际上是该类型,则将参数作为Span<T>(在这种情况下, )的特定特化返回;Span<int>否则,只返回一个空跨度。我们可以看到这种方法适用于数组,但会因 span 而失败。是否也有解决方法可以使用跨度来执行此操作?

标签: c#genericsmemory.net-corecasting

解决方案


如果可以添加where T : struct,则有一种方法:

public static Span<int> TryGetIntSpan<T>(Span<T> x)
    where T : struct
{
    if (typeof(T) == typeof(int))
        return MemoryMarshal.Cast<T, int>(x);

    return Span<int>.Empty;
}

否则,这是另一种方式:

public static Span<int> TryGetIntSpan<T>(Span<T> x)
{
    if (typeof(T) == typeof(int))
        return MemoryMarshal.CreateSpan(ref Unsafe.As<T, int>(ref MemoryMarshal.GetReference(x)), x.Length);

    return Span<int>.Empty;
}

它解构和重建跨度,因为你不能只使用Unsafe.As它,因为Span它是一个ref结构,因此它不能用作类型参数。

检查被if (typeof(T) == typeof(int))JIT 优化掉了。


推荐阅读