Phantom generic constraints in C#

I came across this problematic quite often: I like to overload some method with same parameters for different return types, but .NET refuses generic constraints to sealed classes/primitives. I'll refer to this pattern as phantom generics.

  • I know an ugly workaround: Putting every single interface the type implements behind the where statement.

  • My Question: Is there any way to use explicit types in generics to illustrate the return type and keep methods distinct?

    Here is my code:

    public static class Reinterpret {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe float Cast<T>(int value) where T : float { //'float' is not a valid constraint. A type used as a constraint must be an interface, a non-sealed class or a type parameter.
            return *((float*)&value); //reinterpret the bytes of 'value' to a float
        }
    
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe float Cast<T>(uint value) where T : float { //'float' is not a valid constraint. A type used as a constraint must be an interface, a non-sealed class or a type parameter.
            return *((float*)&value);
        }
    
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe double Cast<T>(long value) where T : double { //'double' is not a valid constraint. A type used as a constraint must be an interface, a non-sealed class or a type parameter.
            return *((double*)&value);
        }
    
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe double Cast<T>(ulong value) where T : double { //'double' is not a valid constraint. A type used as a constraint must be an interface, a non-sealed class or a type parameter.
            return *((double*)&value);
        }
    }
    
  • Jon Skeet
    people
    quotationmark

    Here's one slightly different way of approach it:

    // Constraints just to be vaguely reasonable.
    public static class Reinterpret<T> where T : struct, IComparable<T>
    {
        public T Cast(int value) { ... }
        public T Cast(uint value) { ... }
        public T Cast(float value) { ... }
        public T Cast(double value) { ... }
        // etc       
    }
    

    For the implementation, you could just have a Func<int, T> field, a Func<double, T> field etc, and then have a big static constructor:

    static Reinterpret()
    {
        if (typeof(T) == typeof(int))
        {
            // Assign all the fields using lambda expressions for ints.
            // The actual assignment might be tricky, however - you may
            // need to resort to some ghastly casting, e.g.
            castDouble = (Func<double, T>)(Delegate)(Func<double, int>)
                x => *((double*)&value;
        }
        ....
    }
    

    Then for any type you didn't want to support, the fields would be null. Each Cast method would look like:

    if (castIntMethod != null)
    {
        return castInt(value);
    }
    throw new InvalidOperationException("...");
    

    To be honest, this isn't something I'd really want to do. I'd generally just use BitConverter. But it's an option.

    people

    See more on this question at Stackoverflow