Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting a generic Span<T> to a specific instantiation (e.g. Span<int>) if it actually of that type at runtime

Consider the following C# code:

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;
    }
}

The idea is to return the argument as a particular specialization of Span<T> (in this case, Span<int>) if the argument is actually of that type at runtime; otherwise, just return an empty span. We can see that this approach works with an array, but fails with a span. Is there a workaround to do this with spans as well?

like image 444
Vladimir Reshetnikov Avatar asked Oct 15 '25 12:10

Vladimir Reshetnikov


1 Answers

If you can add where T : struct, there's a method for that:

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;
}

Otherwise, here's another way:

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;
}

It deconstructs and reconstructs the span, as you can't just use Unsafe.As for this, since Span is a ref struct and therefore it's not usable as a type parameter.

The if (typeof(T) == typeof(int)) check is optimized away by the JIT.

like image 128
Lucas Trzesniewski Avatar answered Oct 18 '25 04:10

Lucas Trzesniewski



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!