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

Int16 Int32 Int64 UInt16 UInt32 UInt64

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

喜欢的东西:

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

当前回答

我有一个类似的情况,我需要处理数字类型和字符串;看起来有点奇怪,但就是这样。

和许多人一样,我研究了约束条件,提出了一堆它必须支持的接口。然而,第一,它并不是100%无懈可击的;第二,任何新看到这一长串约束条件的人都会立刻感到困惑。

所以,我的方法是把我所有的逻辑放到一个没有约束的泛型方法中,但是让这个泛型方法是私有的。然后我用公共方法公开它,其中一个方法显式地处理我想处理的类型——在我看来,代码是干净而显式的,例如。

public static string DoSomething(this int input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this decimal input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this double input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this string input, ...) => DoSomethingHelper(input, ...);

private static string DoSomethingHelper<T>(this T input, ....)
{
    // complex logic
}

其他回答

没有办法将模板限制为类型,但是可以根据类型定义不同的操作。作为泛型数值包的一部分,我需要一个泛型类来添加两个值。

    class Something<TCell>
    {
        internal static TCell Sum(TCell first, TCell second)
        {
            if (typeof(TCell) == typeof(int))
                return (TCell)((object)(((int)((object)first)) + ((int)((object)second))));

            if (typeof(TCell) == typeof(double))
                return (TCell)((object)(((double)((object)first)) + ((double)((object)second))));

            return second;
        }
    }

请注意,typeofs是在编译时计算的,因此if语句将被编译器删除。编译器还会删除虚假的类型转换。因此,在编译器中会解析为

        internal static int Sum(int first, int second)
        {
            return first + second;
        }

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

你用的是什么版本的。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

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

十多年后,这个特性终于出现在。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的回答中所讨论的那样,另一种选择(不幸的是,它也有同样的局限性)是使用策略类。

我会使用一个通用的,你可以处理外部…

/// <summary>
/// Generic object copy of the same type
/// </summary>
/// <typeparam name="T">The type of object to copy</typeparam>
/// <param name="ObjectSource">The source object to copy</param>
public T CopyObject<T>(T ObjectSource)
{
    T NewObject = System.Activator.CreateInstance<T>();

    foreach (PropertyInfo p in ObjectSource.GetType().GetProperties())
        NewObject.GetType().GetProperty(p.Name).SetValue(NewObject, p.GetValue(ObjectSource, null), null);

    return NewObject;
}