考虑以下 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> );否则,只需返回一个空跨度。
我们可以看到这种方法适用于数组,但在跨度上失败。是否有解决方法也可以使用跨度执行此操作?

最佳答案

如果您可以添加 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 优化掉。

关于c# - 将泛型 Span<T> 转换为特定实例化(例如 Span<int>),如果它在运行时实际上属于该类型,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59097107/

10-15 14:51