【问题标题】:Converting a generic Span<T> to a specific instantiation (e.g. Span<int>) if it actually of that type at runtime将泛型 Span<T> 转换为特定实例化(例如 Span<int>),如果它在运行时实际上属于该类型
【发布时间】:2019-11-28 23:19:39
【问题描述】:

考虑以下 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&lt;T&gt;(在本例中为Span&lt;int&gt;)的特定特化返回;否则,只返回一个空跨度。 我们可以看到这种方法适用于数组,但会因 span 而失败。是否也有解决方法可以使用跨度执行此操作?

【问题讨论】:

  • 而且它也适用于ArraySegment,所以它不仅仅是一个值类型的问题。

标签: c# generics memory .net-core casting


【解决方案1】:

如果你可以添加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,因为Spanref 结构,因此它不能用作类型参数。

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

【讨论】:

    猜你喜欢
    • 2013-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-25
    • 2013-10-03
    相关资源
    最近更新 更多