C#中的幻像通用约束

Bin*_*man -1 c# generics phantom-types

我经常遇到这个问题:我喜欢为不同的返回类型重载一些具有相同参数的方法,但.NET拒绝对密封类/基元的通用约束.我将这种模式称为phantom generics.

  • 我知道一个丑陋的解决方法:在where语句后面放置每个单独的接口类型.

  • 我的问题:有没有办法在泛型中使用显式类型来说明返回类型并保持方法不同?

    这是我的代码:

    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);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • Jon*_*eet 7

    这是一种稍微不同的方法:

    // 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       
    }
    
    Run Code Online (Sandbox Code Playgroud)

    对于实现,你可以只有一个Func<int, T>字段,一个Func<double, T>字段等,然后有一个很大的静态构造函数:

    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;
        }
        ....
    }
    
    Run Code Online (Sandbox Code Playgroud)

    然后,对于您不想支持的任何类型,字段将为null.每种Cast方法看起来像:

    if (castIntMethod != null)
    {
        return castInt(value);
    }
    throw new InvalidOperationException("...");
    
    Run Code Online (Sandbox Code Playgroud)

    说实话,这不是我真正想做的事情.我一般只是用BitConverter.但这是一个选择.

    • @BinkanSalaryman:那我强烈建议你简单地写下你想要的重载,只在*input*上重载并使用`ToInt32`,`ToDouble`等名称.不,它看起来不像Rust,它看起来不会像C一样 - 它看起来像C#,在C#代码库中更适合它. (2认同)

    Ben*_*igt 6

    泛型不是模板.它们不像模板那样.它们不能像模板一样.

    "幻影"通用参数不会帮助您模拟模板(并且reinterpret_cast无论如何都不是实际模板),因为您很快就会遇到泛型不支持专业化的事实.

    特别是,你问"有没有办法在泛型中使用显式类型来保持方法的独特性?" 并评论说"通用约束......保持[原文如此不同的方法".但他们实际上并没有.这些方法之所以不同,只是因为参数类型不同.泛型是根据重载计算的,它们不会影响重载.