有人能告诉我是否有一种方法可以用泛型将泛型类型参数T限制为:

Int16 Int32 Int64 UInt16 UInt32 UInt64

我知道哪里关键字,但不能找到只有这些类型的接口,

喜欢的东西:

static bool IntegerFunction<T>(T value) where T : INumeric 

当前回答

从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实例化。

其他回答

目前还没有“好的”解决方案。但是,您可以显著地缩小类型参数,以排除许多与您假设的“INumeric”约束不匹配的情况,如上面haacks所示。

static bool IntegerFunction<T>(T值)where T: IComparable, iformatable, IConvertible, IComparable<T>, IEquatable<T>, struct {…

这个练习的意义是什么?

正如人们已经指出的,你可以让一个非泛型函数取最大的项,编译器会自动为你转换较小的整型。

static bool IntegerFunction(Int64 value) { }

如果您的函数处于性能关键的路径上(在我看来,这是不太可能的),您可以为所有需要的函数提供重载。

static bool IntegerFunction(Int64 value) { }
...
static bool IntegerFunction(Int16 value) { }

这个问题有点像常见问题,所以我在维基上发布了这个(因为我之前发布过类似的问题,但这是一个较老的问题);无论如何……

你用的是什么版本的。net ?如果你使用的是。net 3.5,那么我在MiscUtil中有一个通用操作符实现(免费等)。

它有T Add<T>(T x, T y)等方法,以及不同类型上的其他算术变体(如DateTime + TimeSpan)。

此外,这适用于所有内置、提升和定制操作符,并缓存委托以获得性能。

这里有一些关于为什么这很棘手的额外背景。

你可能还想知道动态(4.0)也间接地解决了这个问题。

dynamic x = ..., y = ...
dynamic result = x + y; // does what you expect

十多年后,这个特性终于出现在。net 7中。最通用的接口是INumber<TSelf>而不是innumeric(在系统中。数字名称空间),它不仅包含整数类型。要只接受整数类型,可以考虑使用IBinaryInteger<TSelf>。以你的原型,神秘的IntegerFunction为例:

static bool IntegerFunction<T>(T value) where T : IBinaryInteger<T> {
    return value > T.Zero;
}
Console.WriteLine(IntegerFunction(5));         // True
Console.WriteLine(IntegerFunction((sbyte)-5)); // False
Console.WriteLine(IntegerFunction((ulong)5));  // True

下面的答案(现在已经过时了)是作为一个历史的角度。

c#不支持这一点。在接受Bruce Eckel的采访时,Hejlsberg描述了没有实现该功能的原因:

And it's not clear that the added complexity is worth the small yield that you get. If something you want to do is not directly supported in the constraint system, you can do it with a factory pattern. You could have a Matrix<T>, for example, and in that Matrix you would like to define a dot product method. That of course that means you ultimately need to understand how to multiply two Ts, but you can't say that as a constraint, at least not if T is int, double, or float. But what you could do is have your Matrix take as an argument a Calculator<T>, and in Calculator<T>, have a method called multiply. You go implement that and you pass it to the Matrix.

然而,这会导致相当复杂的代码,用户必须为他们想要使用的每个T提供自己的Calculator<T>实现。只要它不需要是可扩展的,也就是说,如果你只想支持固定数量的类型,比如int和double,你可以使用一个相对简单的接口:

var mat = new Matrix<int>(w, h);

(GitHub Gist中的最小实现。)

然而,一旦您希望用户能够提供他们自己的自定义类型,您就需要打开这个实现,以便用户能够提供他们自己的Calculator实例。例如,要实例化一个使用自定义十进制浮点数实现DFP的矩阵,你必须编写以下代码:

var mat = new Matrix<DFP>(DfpCalculator.Instance, w, h);

实现DfpCalculator的所有成员:ICalculator<DFP>。

正如Sergey Shandar的回答中所讨论的那样,另一种选择(不幸的是,它也有同样的局限性)是使用策略类。

这是没有限制的。对于任何想要使用泛型进行数值计算的人来说,这都是一个真正的问题。

更进一步说,我们需要

static bool GenericFunction<T>(T value) 
    where T : operators( +, -, /, * )

甚至

static bool GenericFunction<T>(T value) 
    where T : Add, Subtract

不幸的是,你只有接口,基类和关键字struct(必须是值类型),class(必须是引用类型)和new()(必须有默认构造函数)

你可以把数字包装在其他东西(类似于INullable<T>),就像这里的codeproject。


您可以在运行时应用该限制(通过对操作符进行反射或检查类型),但这首先就失去了使用泛型的优势。