C# 中的幻像泛型约束
Phantom generic constraints in C#
我经常遇到这个问题:我喜欢为不同的 return 类型重载一些具有相同参数的方法,但 .NET 拒绝对密封 classes/primitives 的通用约束。我将此模式称为 phantom generics
.
我知道一个丑陋的解决方法:将类型实现的每个接口都放在 where
语句后面。
我的问题:有什么方法可以在泛型中使用显式类型来说明 return 类型并保持方法不同?
这是我的代码:
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);
}
}
这是一种稍微不同的方法:
// 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
}
对于 实现 ,你可以有一个 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;
}
....
}
然后对于您不想支持的任何类型,字段将为空。每个 Cast
方法看起来像:
if (castIntMethod != null)
{
return castInt(value);
}
throw new InvalidOperationException("...");
老实说,这不是我真正想做的事情。我通常只使用 BitConverter
。但这是一个选项。
泛型不是模板。它们不像模板。不能使它们像模板一样工作。
"phantom" 通用参数不会帮助您模拟模板(无论如何 reinterpret_cast
不是实际模板),因为您很快就会 运行事实上 泛型不支持专业化。
特别是,您问 "Is there any way to use explicit types in generics to ... keep methods distinct?" 并评论说 "the generic constraints ... keeps [sic] the methods distinct"。但他们实际上没有。这些方法之所以不同,只是因为参数类型不同。泛型是从重载中计算出来的,它们不影响重载。
我经常遇到这个问题:我喜欢为不同的 return 类型重载一些具有相同参数的方法,但 .NET 拒绝对密封 classes/primitives 的通用约束。我将此模式称为 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);
}
}
这是一种稍微不同的方法:
// 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
}
对于 实现 ,你可以有一个 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;
}
....
}
然后对于您不想支持的任何类型,字段将为空。每个 Cast
方法看起来像:
if (castIntMethod != null)
{
return castInt(value);
}
throw new InvalidOperationException("...");
老实说,这不是我真正想做的事情。我通常只使用 BitConverter
。但这是一个选项。
泛型不是模板。它们不像模板。不能使它们像模板一样工作。
"phantom" 通用参数不会帮助您模拟模板(无论如何 reinterpret_cast
不是实际模板),因为您很快就会 运行事实上 泛型不支持专业化。
特别是,您问 "Is there any way to use explicit types in generics to ... keep methods distinct?" 并评论说 "the generic constraints ... keeps [sic] the methods distinct"。但他们实际上没有。这些方法之所以不同,只是因为参数类型不同。泛型是从重载中计算出来的,它们不影响重载。