-1

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);
        }
    }
    
  • Binkan Salaryman
    • 3,008
    • 1
    • 17
    • 29
    • 7
      It's unclear to me what you're trying to achieve here, to be honest. It looks like you'd be a lot better off just giving your methods different names. – Jon Skeet Mar 05 '15 at 21:19
    • 1
      Hi there, it's you again! And I guess you voted my question down... And no, renaming is not the preferred option, as I like to implement a c-style `reinterpret_cast` in C#. – Binkan Salaryman Mar 05 '15 at 21:22
    • 1
      I'm guessing you don't realize just who Jon Skeet is - the last time Jon Skeet was wrong on a C# question I hadn't even learned the language yet. Heeding his advice ia one of the best ways to become a better C# programmer. – Pieter Geerkens Mar 05 '15 at 21:23
    • No, I didn't vote it down - and while *you* may not prefer different names, I know that I (as a method consumer) would. Trying to port one idiom to another platform is often a bad idea, IME. That's not to say it's not fun to try odd things, but for production code I'd really try to steer away from this. – Jon Skeet Mar 05 '15 at 21:23
    • 1
      I find this interesting, but it is indeed unclear what you are trying to achieve. – Codor Mar 05 '15 at 21:23
    • What can I do to improve to clearify the matter, then? – Binkan Salaryman Mar 05 '15 at 21:33
    • What is the purpose of the generics when they're never referenced in the method itself other than the generic type specifier? Have you checked out `Convert.ToDouble` and `Convert.ToSingle`? – Matthew Mar 05 '15 at 21:36
    • As I wrote, the generic constraints fungate as return type indicator and keeps the methods distinct. – Binkan Salaryman Mar 05 '15 at 21:37
    • 2
      @PieterGeerkens Jon Skeet is never wrong, when someone/something disagrees, the universe alters so it matches what he said :) – BradleyDotNET Mar 05 '15 at 21:45
    • You're a fool to believe in god - I believe in codez – Binkan Salaryman Mar 05 '15 at 21:53
    • 1
      You may find this interesting: http://stackoverflow.com/questions/32664/is-there-a-constraint-that-restricts-my-generic-method-to-numeric-types/22425077#22425077 – Jeroen Vannevel Mar 06 '15 at 03:25
    • Thanks, that was interesting to read! – Binkan Salaryman Mar 06 '15 at 10:42

    2 Answers2

    7

    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.

    Jon Skeet
    • 1,421,763
    • 867
    • 9,128
    • 9,194
    • Pretty good answer. But in this specific case I like to write fast, low-level code with `inline`d methods. As beeing inlined, the generic constraints will only be known at *compile time* (see the rust article I mentioned in the article), plus I don't want any overhead here. – Binkan Salaryman Mar 05 '15 at 21:47
    • 2
      @BinkanSalaryman: Then I strongly recommend you simply write the overloads you want, overload on *input* only and using names like `ToInt32`, `ToDouble` etc. No, it won't look like Rust, and it won't look like C - it'll look like C#, and be all the more appropriate in a C# codebase for it. – Jon Skeet Mar 05 '15 at 21:59
    6

    Generics are not templates. They do not act like templates. They cannot be made to act like templates.

    A "phantom" generic parameter is not going to help you simulate templates (and reinterpret_cast is not an actual template, anyway), because you soon are going to run into the fact that generics do not support specialization.

    In particular, you asked "Is there any way to use explicit types in generics to ... keep methods distinct?" and commented that "the generic constraints ... keeps [sic] the methods distinct". But they actually do not. These methods are distinct only because the argument types are different. Generics are computed from overloads, they do not influence overloading.

    Ben Voigt
    • 277,958
    • 43
    • 419
    • 720