有人能告诉我是否有一种方法可以用泛型将泛型类型参数T限制为:
Int16 Int32 Int64 UInt16 UInt32 UInt64
我知道哪里关键字,但不能找到只有这些类型的接口,
喜欢的东西:
static bool IntegerFunction<T>(T value) where T : INumeric
有人能告诉我是否有一种方法可以用泛型将泛型类型参数T限制为:
Int16 Int32 Int64 UInt16 UInt32 UInt64
我知道哪里关键字,但不能找到只有这些类型的接口,
喜欢的东西:
static bool IntegerFunction<T>(T value) where T : INumeric
当前回答
不幸的是,. net并没有提供一种本地的方法。
为了解决这个问题,我创建了OSS库generatics,它为以下内置数字类型及其可空等价提供了大多数标准数字操作,并能够添加对其他数字类型的支持。
sbyte、byte、short、ushort、int、uint、long、ulong、float、double、decimal、BigInteger
其性能相当于特定于数值类型的解决方案,允许您创建高效的通用数值算法。
下面是一个代码使用示例。
public static T Sum(T[] items)
{
T sum = Number.Zero<T>();
foreach (T item in items)
{
sum = Number.Add(sum, item);
}
return sum;
}
public static T SumAlt(T[] items)
{
// implicit conversion to Number<T>
Number<T> sum = Number.Zero<T>();
foreach (T item in items)
{
// operator support
sum += item;
}
// implicit conversion to T
return sum;
}
其他回答
.NET 6有一个预览功能:
https://devblogs.microsoft.com/dotnet/preview-features-in-net-6-generic-math/#generic-math
下面是文章中的一个例子:
static T Add<T>(T left, T right)
where T : INumber<T>
{
return left + right;
}
INumber是一个实现其他接口的接口,比如IAdditionOperators,它允许通用的+用法。现在这是可能的,因为另一个预览特性是接口中的静态抽象,因为+操作符重载是一个静态方法:
/// <summary>Defines a mechanism for computing the sum of two values.</summary>
/// <typeparam name="TSelf">The type that implements this interface.</typeparam>
/// <typeparam name="TOther">The type that will be added to <typeparamref name="TSelf" />.</typeparam>
/// <typeparam name="TResult">The type that contains the sum of <typeparamref name="TSelf" /> and <typeparamref name="TOther" />.</typeparam>
[RequiresPreviewFeatures(Number.PreviewFeatureMessage, Url = Number.PreviewFeatureUrl)]
public interface IAdditionOperators<TSelf, TOther, TResult>
where TSelf : IAdditionOperators<TSelf, TOther, TResult>
{
/// <summary>Adds two values together to compute their sum.</summary>
/// <param name="left">The value to which <paramref name="right" /> is added.</param>
/// <param name="right">The value which is added to <paramref name="left" />.</param>
/// <returns>The sum of <paramref name="left" /> and <paramref name="right" />.</returns>
static abstract TResult operator +(TSelf left, TOther right);
}
不幸的是,. net并没有提供一种本地的方法。
为了解决这个问题,我创建了OSS库generatics,它为以下内置数字类型及其可空等价提供了大多数标准数字操作,并能够添加对其他数字类型的支持。
sbyte、byte、short、ushort、int、uint、long、ulong、float、double、decimal、BigInteger
其性能相当于特定于数值类型的解决方案,允许您创建高效的通用数值算法。
下面是一个代码使用示例。
public static T Sum(T[] items)
{
T sum = Number.Zero<T>();
foreach (T item in items)
{
sum = Number.Add(sum, item);
}
return sum;
}
public static T SumAlt(T[] items)
{
// implicit conversion to Number<T>
Number<T> sum = Number.Zero<T>();
foreach (T item in items)
{
// operator support
sum += item;
}
// implicit conversion to T
return sum;
}
这个练习的意义是什么?
正如人们已经指出的,你可以让一个非泛型函数取最大的项,编译器会自动为你转换较小的整型。
static bool IntegerFunction(Int64 value) { }
如果您的函数处于性能关键的路径上(在我看来,这是不太可能的),您可以为所有需要的函数提供重载。
static bool IntegerFunction(Int64 value) { }
...
static bool IntegerFunction(Int16 value) { }
所有数值类型都是实现IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable的结构体。然而,DateTime也是如此。
所以这个泛型扩展方法是可能的:
public static bool IsNumeric<T>(this T value) where T : struct, IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable =>
typeof(T) != typeof(DateTime);
但是对于实现这些接口的结构体,它将失败,例如:
public struct Foo : IComparable, IComparable<Foo>, IConvertible, IEquatable<Foo>, IFormattable { /* ... */ }
这个非泛型的替代方法性能较差,但保证可以工作:
public static bool IsNumeric(this Type type) =>
type == typeof(sbyte) || type == typeof(byte) ||
type == typeof(short) || type == typeof(ushort) ||
type == typeof(int) || type == typeof(uint) ||
type == typeof(long) || type == typeof(ulong) ||
type == typeof(float) ||
type == typeof(double) ||
type == typeof(decimal);
从c# 7.3开始,您可以使用更接近的非托管约束来指定类型形参是非指针、非空的非托管类型。
class SomeGeneric<T> where T : unmanaged
{
//...
}
非托管约束意味着结构约束,并且不能与结构或new()约束组合。
如果是以下类型之一,则该类型为非托管类型:
Sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、decimal或bool 任何enum类型 任何指针类型 任何只包含非托管类型字段的用户定义结构类型,在c# 7.3及更早版本中,都不是构造类型(至少包含一个类型参数的类型)。
为了进一步限制和消除没有实现IComparable的指针和用户定义类型add IComparable(但enum仍然派生自IComparable,因此通过添加IEquatable < T >来限制enum,您可以根据您的情况进一步添加额外的接口。Unmanaged可以让这个列表更短):
class SomeGeneric<T> where T : unmanaged, IComparable, IEquatable<T>
{
//...
}
但是这并没有阻止DateTime实例化。