是否存在将我的泛型方法限制为数字类型的约束?

时间:2008-08-28 16:04:50

标签: c# generics constraints

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

  • Int16
  • Int32
  • Int64
  • UInt16
  • UInt32
  • UInt64

我知道where关键字,但找不到这些类型的界面,

类似的东西:

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

23 个答案:

答案 0 :(得分:126)

C#不支持此功能。 Hejlsberg描述了不实现特征in an interview with Bruce Eckel的原因:

  

并不清楚增加的复杂性是否值得您获得的小收益。如果约束系统不直接支持您想要执行的操作,则可以使用工厂模式执行此操作。例如,您可以拥有Matrix<T>,并且在Matrix中您想要定义点积方法。那当然这意味着你最终需要了解如何将两个T相乘,但你不能说这是一个约束,至少不是Tint,{{ 1}}或double。但是,您可以做的是将float作为Matrix的参数,并在Calculator<T>中使用名为Calculator<T>的方法。你去实现它并将它传递给multiply

但是,这会导致相当复杂的代码,用户必须为他们想要使用的每个Matrix提供自己的Calculator<T>实现。只要它不必是可扩展的,即如果您只想支持固定数量的类型,例如Tint,您就可以使用相对简单的界面:< / p>

double

Minimal implementation in a GitHub Gist.

但是,只要您希望用户能够提供自己的自定义类型,就需要打开此实现,以便用户可以提供自己的var mat = new Matrix<int>(w, h); 实例。例如,要实例化使用自定义十进制浮点实现的矩阵Calculator,您必须编写此代码:

DFP

...并实施var mat = new Matrix<DFP>(DfpCalculator.Instance, w, h); 的所有成员。

另一种不幸的是,它有着相同的限制,就是使用策略类as discussed in Sergey Shandar’s answer

答案 1 :(得分:90)

考虑到这个问题的普及以及这种功能背后的兴趣,我很惊讶地发现T4还没有答案。

在这个示例代码中,我将演示一个非常简单的示例,说明如何使用强大的模板引擎来完成编译器在后台使用泛型执行的操作。

您可以简单地为您喜欢的每种类型生成所需的函数,并相应地使用它(在编译时!),而不是通过箍和牺牲编译时的确定性。

为了做到这一点:

  • 创建名为 GenericNumberMethodTemplate.tt 的新文本模板文件。
  • 删除自动生成的代码(您将保留大部分代码,但不需要某些代码)。
  • 添加以下代码段:
<#@ template language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>

<# Type[] types = new[] {
    typeof(Int16), typeof(Int32), typeof(Int64),
    typeof(UInt16), typeof(UInt32), typeof(UInt64)
    };
#>

using System;
public static class MaxMath {
    <# foreach (var type in types) { 
    #>
        public static <#= type.Name #> Max (<#= type.Name #> val1, <#= type.Name #> val2) {
            return val1 > val2 ? val1 : val2;
        }
    <#
    } #>
}

就是这样。你现在就完成了。

保存此文件会自动将其编译为此源文件:

using System;
public static class MaxMath {
    public static Int16 Max (Int16 val1, Int16 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static Int32 Max (Int32 val1, Int32 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static Int64 Max (Int64 val1, Int64 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static UInt16 Max (UInt16 val1, UInt16 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static UInt32 Max (UInt32 val1, UInt32 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static UInt64 Max (UInt64 val1, UInt64 val2) {
        return val1 > val2 ? val1 : val2;
    }
}

main方法中,您可以验证您是否具有编译时确定性:

namespace TTTTTest
{
    class Program
    {
        static void Main(string[] args)
        {
            long val1 = 5L;
            long val2 = 10L;
            Console.WriteLine(MaxMath.Max(val1, val2));
            Console.Read();
        }
    }
}

enter image description here

我会提前一句话:不,这不违反DRY原则。 DRY原则是为了防止人们在多个地方复制代码,导致应用程序变得难以维护。

这里的情况并非如此:如果您想要进行更改,那么您只需更改模板(您所有代的单一来源!)即可完成。

为了将它与您自己的自定义定义一起使用,请向生成的代码添加名称空间声明(确保它与您定义自己的实现的声明相同),并将该类标记为{{1} }。然后,将这些行添加到模板文件中,以便它包含在最终编译中:

partial

老实说:这很酷。

免责声明:此示例受Metaprogramming in .NET by Kevin Hazzard and Jason Bock, Manning Publications的影响很大。

答案 2 :(得分:82)

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

我会更进一步说我们需要

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

甚至

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

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

您可以将该数字换成其他内容(类似于INullable<T>),例如here on codeproject


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

答案 3 :(得分:55)

使用政策的解决方法:

interface INumericPolicy<T>
{
    T Zero();
    T Add(T a, T b);
    // add more functions here, such as multiplication etc.
}

struct NumericPolicies:
    INumericPolicy<int>,
    INumericPolicy<long>
    // add more INumericPolicy<> for different numeric types.
{
    int INumericPolicy<int>.Zero() { return 0; }
    long INumericPolicy<long>.Zero() { return 0; }
    int INumericPolicy<int>.Add(int a, int b) { return a + b; }
    long INumericPolicy<long>.Add(long a, long b) { return a + b; }
    // implement all functions from INumericPolicy<> interfaces.

    public static NumericPolicies Instance = new NumericPolicies();
}

算法:

static class Algorithms
{
    public static T Sum<P, T>(this P p, params T[] a)
        where P: INumericPolicy<T>
    {
        var r = p.Zero();
        foreach(var i in a)
        {
            r = p.Add(r, i);
        }
        return r;
    }

}

用法:

int i = NumericPolicies.Instance.Sum(1, 2, 3, 4, 5);
long l = NumericPolicies.Instance.Sum(1L, 2, 3, 4, 5);
NumericPolicies.Instance.Sum("www", "") // compile-time error.

解决方案是编译时安全的。 CityLizard Framework提供.NET 4.0的编译版本。该文件是lib / NETFramework4.0 / CityLizard.Policy.dll。

它也可以在Nuget中使用:https://www.nuget.org/packages/CityLizard/。请参阅 CityLizard.Policy.I 结构。

答案 4 :(得分:15)

这个问题有点像常见问题解答之一,所以我将其作为维基发布(因为我以前发过类似的内容,但这是较旧的);反正...

您使用的是哪个版本的.NET?如果您使用的是.NET 3.5,那么generic operators implementation中有MiscUtil(免费等)。

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

此外,这适用于所有内置,提升和定制的运算符,并缓存代表的性能。

关于为什么这很棘手的一些额外背景是here

您可能还想知道dynamic(4.0)间接地解决了这个问题 - 即

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

答案 5 :(得分:14)

不幸的是,您只能在此实例的where子句中指定struct。看起来很奇怪你不能具体指定Int16,Int32等,但我确信在if子句中不允许值类型的决定存在一些深层的实现原因。

我想唯一的解决方案是进行运行时检查,遗憾的是这可以防止在编译时拾取问题。这就像: -

static bool IntegerFunction<T>(T value) where T : struct {
  if (typeof(T) != typeof(Int16)  &&
      typeof(T) != typeof(Int32)  &&
      typeof(T) != typeof(Int64)  &&
      typeof(T) != typeof(UInt16) &&
      typeof(T) != typeof(UInt32) &&
      typeof(T) != typeof(UInt64)) {
    throw new ArgumentException(
      string.Format("Type '{0}' is not valid.", typeof(T).ToString()));
  }

  // Rest of code...
}

我知道这有点难看,但至少提供了所需的约束。

我还会研究这种实现可能带来的性能影响,也许还有更快的方法。

答案 6 :(得分:13)

从C#7.3开始,您可以使用更紧密的近似值-非托管约束来指定类型参数是非指针,不可为空的{{3} }类型。

class SomeGeneric<T> where T : unmanaged
{
//...
}

非托管约束意味着结构约束,不能与struct约束或new()约束结合使用。

如果类型是以下任何类型,则它是非托管类型:

  • sbyte,byte,short,ushort,int,uint,long,ulong,char,float,double,decimal或bool
  • 任何枚举类型
  • 任何指针类型
  • 任何用户定义的结构类型都仅包含非托管类型的字段,并且在C#7.3及更早版本中,不是构造类型(至少包含一个类型参数的类型)

要进一步限制并消除未实现IComparable的指针和用户定义的类型,请添加 IComparable (但枚举仍是从IComparable派生的,因此可以通过添加IEquatable 来限制枚举,进一步根据您的情况并添加其他接口。unmanaged可以使此列表更短):

    class SomeGeneric<T> where T : unmanaged, IComparable, IEquatable<T>
    {
    //...
    }

但这不会阻止DateTime实例化。

答案 7 :(得分:11)

你可以做的最接近的是

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

不确定您是否可以执行以下操作

static bool IntegerFunction<T>(T value) where T: struct, IComparable
, IFormattable, IConvertible, IComparable<T>, IEquatable<T>

对于某些特定的东西,为什么不对每种类型都有重载,列表太短,内存占用可能会少。

答案 8 :(得分:5)

主题很老,但对于将来的读者来说:

此功能与Discriminated Unions紧密相关,到目前为止,尚未在C#中实现。我在这里找到了问题:

https://github.com/dotnet/csharplang/issues/113

此问题仍未解决,并且已计划为C# 10

提供功能

所以我们仍然需要再等一会儿,但是释放后,您可以按照以下方式进行操作:

static bool IntegerFunction<T>(T value) where T : Int16 | Int32 | Int64 | ...

答案 9 :(得分:4)

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

    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;
        }

答案 10 :(得分:3)

我创建了一个小的库功能来解决这些问题:

而不是:

public T DifficultCalculation<T>(T a, T b)
{
    T result = a * b + a; // <== WILL NOT COMPILE!
    return result;
}
Console.WriteLine(DifficultCalculation(2, 3)); // Should result in 8.

你可以写:

public T DifficultCalculation<T>(Number<T> a, Number<T> b)
{
    Number<T> result = a * b + a;
    return (T)result;
}
Console.WriteLine(DifficultCalculation(2, 3)); // Results in 8.

您可以在此处找到源代码:https://codereview.stackexchange.com/questions/26022/improvement-requested-for-generic-calculator-and-generic-number

答案 11 :(得分:2)

我想和samjudson一样,为什么只对整数?如果是这种情况,您可能需要创建一个帮助程序类或类似的东西来保存所需的所有类型。

如果您想要的只是整数,请不要使用通用的,不是通用的;或者更好的是,通过检查其类型拒绝任何其他类型。

答案 12 :(得分:2)

不幸的是,.NET没有提供一种本地执行此操作的方法。

为解决此问题,我创建了OSS库Genumerics,该库为以下内置数字类型及其可为空的等效项提供了大多数标准数字运算,并能够添加对其他数字类型的支持。

sbytebyteshortushortintuintlongulongfloatdoubledecimalBigInteger

性能等同于特定于数字类型的解决方案,可让您创建高效的通用数字算法。

这是代码用法的一个例子。

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;
}

答案 13 :(得分:1)

.NET数字基元类型不共享任何允许它们用于计算的公共接口。可以定义自己的接口(例如ISignedWholeNumber)来执行此类操作,定义包含单个Int16Int32等的结构并实现这些接口,然后有接受约束到ISignedWholeNumber的泛型类型的方法,但必须将数值转换为结构类型可能会造成麻烦。

另一种方法是使用静态属性Int64Converter<T>定义静态类bool Available {get;};,为Int64 GetInt64(T value)T FromInt64(Int64 value)bool TryStoreInt64(Int64 value, ref T dest)定义静态委托。类构造函数可以使用硬编码来加载已知类型的委托,并且可能使用Reflection来测试类型T是否实现具有正确名称和签名的方法(如果它类似于包含{{的结构的类型) 1}}并表示一个数字,但具有自定义Int64方法。这种方法将失去与编译时类型检查相关的优点,但仍然设法避免装箱操作,并且每种类型只需要“检查”一次。之后,与该类型相关联的操作将替换为委托调度。

答案 14 :(得分:1)

如果您使用的是.NET 4.0及更高版本,那么您可以使用动态作为方法参数并检查在运行时中传递的动态参数类型是数字/整数类型。

如果传递的动态的类型为而不是数字/整数类型,则抛出异常。

实现这个想法的示例简短代码如下:

using System;
public class InvalidArgumentException : Exception
{
    public InvalidArgumentException(string message) : base(message) {}
}
public class InvalidArgumentTypeException : InvalidArgumentException
{
    public InvalidArgumentTypeException(string message) : base(message) {}
}
public class ArgumentTypeNotIntegerException : InvalidArgumentTypeException
{
    public ArgumentTypeNotIntegerException(string message) : base(message) {}
}
public static class Program
{
    private static bool IntegerFunction(dynamic n)
    {
        if (n.GetType() != typeof(Int16) &&
            n.GetType() != typeof(Int32) &&
            n.GetType() != typeof(Int64) &&
            n.GetType() != typeof(UInt16) &&
            n.GetType() != typeof(UInt32) &&
            n.GetType() != typeof(UInt64))
            throw new ArgumentTypeNotIntegerException("argument type is not integer type");
        //code that implements IntegerFunction goes here
    }
    private static void Main()
    {
         Console.WriteLine("{0}",IntegerFunction(0)); //Compiles, no run time error and first line of output buffer is either "True" or "False" depends on the code that implements "Program.IntegerFunction" static method.
         Console.WriteLine("{0}",IntegerFunction("string")); //Also compiles but it is run time error and exception of type "ArgumentTypeNotIntegerException" is thrown here.
         Console.WriteLine("This is the last Console.WriteLine output"); //Never reached and executed due the run time error and the exception thrown on the second line of Program.Main static method.
    }

当然,此解决方案仅在运行时工作,但从不在编译时工作。

如果您想要一个始终在编译时工作且从不在运行时间的解决方案,那么您必须将动态包装为重载 public 的公共结构/类构造函数只接受所需类型的参数,并为结构/类赋予适当的名称。

包装的动态始终是私有类/ struct的成员,它是struct / class的唯一成员和唯一的名称struct / class的成员是&#34; value&#34;。

如果需要,您还必须定义和实现 public 方法和/或运算符,这些方法和/或运算符使用类/结构的私有动态成员所需的类型。

结构/类还有特殊/唯一构造函数,它接受动态作为参数,初始化它的唯一私有动态成员#&# 34;值&#34;但是这个构造函数的修饰符当然是私有

一旦类/结构准备好,就将参数的IntegerFunction类型定义为已定义的类/结构。

实现这个想法的示例 long 代码如下:

using System;
public struct Integer
{
    private dynamic value;
    private Integer(dynamic n) { this.value = n; }
    public Integer(Int16 n) { this.value = n; }
    public Integer(Int32 n) { this.value = n; }
    public Integer(Int64 n) { this.value = n; }
    public Integer(UInt16 n) { this.value = n; }
    public Integer(UInt32 n) { this.value = n; }
    public Integer(UInt64 n) { this.value = n; }
    public Integer(Integer n) { this.value = n.value; }
    public static implicit operator Int16(Integer n) { return n.value; }
    public static implicit operator Int32(Integer n) { return n.value; }
    public static implicit operator Int64(Integer n) { return n.value; }
    public static implicit operator UInt16(Integer n) { return n.value; }
    public static implicit operator UInt32(Integer n) { return n.value; }
    public static implicit operator UInt64(Integer n) { return n.value; }
    public static Integer operator +(Integer x, Int16 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, Int32 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, Int64 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, UInt16 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, UInt32 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, UInt64 y) { return new Integer(x.value + y); }
    public static Integer operator -(Integer x, Int16 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, Int32 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, Int64 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, UInt16 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, UInt32 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, UInt64 y) { return new Integer(x.value - y); }
    public static Integer operator *(Integer x, Int16 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, Int32 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, Int64 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, UInt16 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, UInt32 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, UInt64 y) { return new Integer(x.value * y); }
    public static Integer operator /(Integer x, Int16 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, Int32 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, Int64 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, UInt16 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, UInt32 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, UInt64 y) { return new Integer(x.value / y); }
    public static Integer operator %(Integer x, Int16 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, Int32 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, Int64 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, UInt16 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, UInt32 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, UInt64 y) { return new Integer(x.value % y); }
    public static Integer operator +(Integer x, Integer y) { return new Integer(x.value + y.value); }
    public static Integer operator -(Integer x, Integer y) { return new Integer(x.value - y.value); }
    public static Integer operator *(Integer x, Integer y) { return new Integer(x.value * y.value); }
    public static Integer operator /(Integer x, Integer y) { return new Integer(x.value / y.value); }
    public static Integer operator %(Integer x, Integer y) { return new Integer(x.value % y.value); }
    public static bool operator ==(Integer x, Int16 y) { return x.value == y; }
    public static bool operator !=(Integer x, Int16 y) { return x.value != y; }
    public static bool operator ==(Integer x, Int32 y) { return x.value == y; }
    public static bool operator !=(Integer x, Int32 y) { return x.value != y; }
    public static bool operator ==(Integer x, Int64 y) { return x.value == y; }
    public static bool operator !=(Integer x, Int64 y) { return x.value != y; }
    public static bool operator ==(Integer x, UInt16 y) { return x.value == y; }
    public static bool operator !=(Integer x, UInt16 y) { return x.value != y; }
    public static bool operator ==(Integer x, UInt32 y) { return x.value == y; }
    public static bool operator !=(Integer x, UInt32 y) { return x.value != y; }
    public static bool operator ==(Integer x, UInt64 y) { return x.value == y; }
    public static bool operator !=(Integer x, UInt64 y) { return x.value != y; }
    public static bool operator ==(Integer x, Integer y) { return x.value == y.value; }
    public static bool operator !=(Integer x, Integer y) { return x.value != y.value; }
    public override bool Equals(object obj) { return this == (Integer)obj; }
    public override int GetHashCode() { return this.value.GetHashCode(); }
    public override string ToString() { return this.value.ToString(); }
    public static bool operator >(Integer x, Int16 y) { return x.value > y; }
    public static bool operator <(Integer x, Int16 y) { return x.value < y; }
    public static bool operator >(Integer x, Int32 y) { return x.value > y; }
    public static bool operator <(Integer x, Int32 y) { return x.value < y; }
    public static bool operator >(Integer x, Int64 y) { return x.value > y; }
    public static bool operator <(Integer x, Int64 y) { return x.value < y; }
    public static bool operator >(Integer x, UInt16 y) { return x.value > y; }
    public static bool operator <(Integer x, UInt16 y) { return x.value < y; }
    public static bool operator >(Integer x, UInt32 y) { return x.value > y; }
    public static bool operator <(Integer x, UInt32 y) { return x.value < y; }
    public static bool operator >(Integer x, UInt64 y) { return x.value > y; }
    public static bool operator <(Integer x, UInt64 y) { return x.value < y; }
    public static bool operator >(Integer x, Integer y) { return x.value > y.value; }
    public static bool operator <(Integer x, Integer y) { return x.value < y.value; }
    public static bool operator >=(Integer x, Int16 y) { return x.value >= y; }
    public static bool operator <=(Integer x, Int16 y) { return x.value <= y; }
    public static bool operator >=(Integer x, Int32 y) { return x.value >= y; }
    public static bool operator <=(Integer x, Int32 y) { return x.value <= y; }
    public static bool operator >=(Integer x, Int64 y) { return x.value >= y; }
    public static bool operator <=(Integer x, Int64 y) { return x.value <= y; }
    public static bool operator >=(Integer x, UInt16 y) { return x.value >= y; }
    public static bool operator <=(Integer x, UInt16 y) { return x.value <= y; }
    public static bool operator >=(Integer x, UInt32 y) { return x.value >= y; }
    public static bool operator <=(Integer x, UInt32 y) { return x.value <= y; }
    public static bool operator >=(Integer x, UInt64 y) { return x.value >= y; }
    public static bool operator <=(Integer x, UInt64 y) { return x.value <= y; }
    public static bool operator >=(Integer x, Integer y) { return x.value >= y.value; }
    public static bool operator <=(Integer x, Integer y) { return x.value <= y.value; }
    public static Integer operator +(Int16 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(Int32 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(Int64 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(UInt16 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(UInt32 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(UInt64 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator -(Int16 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(Int32 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(Int64 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(UInt16 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(UInt32 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(UInt64 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator *(Int16 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(Int32 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(Int64 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(UInt16 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(UInt32 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(UInt64 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator /(Int16 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(Int32 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(Int64 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(UInt16 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(UInt32 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(UInt64 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator %(Int16 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(Int32 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(Int64 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(UInt16 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(UInt32 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(UInt64 x, Integer y) { return new Integer(x % y.value); }
    public static bool operator ==(Int16 x, Integer y) { return x == y.value; }
    public static bool operator !=(Int16 x, Integer y) { return x != y.value; }
    public static bool operator ==(Int32 x, Integer y) { return x == y.value; }
    public static bool operator !=(Int32 x, Integer y) { return x != y.value; }
    public static bool operator ==(Int64 x, Integer y) { return x == y.value; }
    public static bool operator !=(Int64 x, Integer y) { return x != y.value; }
    public static bool operator ==(UInt16 x, Integer y) { return x == y.value; }
    public static bool operator !=(UInt16 x, Integer y) { return x != y.value; }
    public static bool operator ==(UInt32 x, Integer y) { return x == y.value; }
    public static bool operator !=(UInt32 x, Integer y) { return x != y.value; }
    public static bool operator ==(UInt64 x, Integer y) { return x == y.value; }
    public static bool operator !=(UInt64 x, Integer y) { return x != y.value; }
    public static bool operator >(Int16 x, Integer y) { return x > y.value; }
    public static bool operator <(Int16 x, Integer y) { return x < y.value; }
    public static bool operator >(Int32 x, Integer y) { return x > y.value; }
    public static bool operator <(Int32 x, Integer y) { return x < y.value; }
    public static bool operator >(Int64 x, Integer y) { return x > y.value; }
    public static bool operator <(Int64 x, Integer y) { return x < y.value; }
    public static bool operator >(UInt16 x, Integer y) { return x > y.value; }
    public static bool operator <(UInt16 x, Integer y) { return x < y.value; }
    public static bool operator >(UInt32 x, Integer y) { return x > y.value; }
    public static bool operator <(UInt32 x, Integer y) { return x < y.value; }
    public static bool operator >(UInt64 x, Integer y) { return x > y.value; }
    public static bool operator <(UInt64 x, Integer y) { return x < y.value; }
    public static bool operator >=(Int16 x, Integer y) { return x >= y.value; }
    public static bool operator <=(Int16 x, Integer y) { return x <= y.value; }
    public static bool operator >=(Int32 x, Integer y) { return x >= y.value; }
    public static bool operator <=(Int32 x, Integer y) { return x <= y.value; }
    public static bool operator >=(Int64 x, Integer y) { return x >= y.value; }
    public static bool operator <=(Int64 x, Integer y) { return x <= y.value; }
    public static bool operator >=(UInt16 x, Integer y) { return x >= y.value; }
    public static bool operator <=(UInt16 x, Integer y) { return x <= y.value; }
    public static bool operator >=(UInt32 x, Integer y) { return x >= y.value; }
    public static bool operator <=(UInt32 x, Integer y) { return x <= y.value; }
    public static bool operator >=(UInt64 x, Integer y) { return x >= y.value; }
    public static bool operator <=(UInt64 x, Integer y) { return x <= y.value; }
}
public static class Program
{
    private static bool IntegerFunction(Integer n)
    {
        //code that implements IntegerFunction goes here
        //note that there is NO code that checks the type of n in rum time, because it is NOT needed anymore 
    }
    private static void Main()
    {
        Console.WriteLine("{0}",IntegerFunction(0)); //compile error: there is no overloaded METHOD for objects of type "int" and no implicit conversion from any object, including "int", to "Integer" is known.
        Console.WriteLine("{0}",IntegerFunction(new Integer(0))); //both compiles and no run time error
        Console.WriteLine("{0}",IntegerFunction("string")); //compile error: there is no overloaded METHOD for objects of type "string" and no implicit conversion from any object, including "string", to "Integer" is known.
        Console.WriteLine("{0}",IntegerFunction(new Integer("string"))); //compile error: there is no overloaded CONSTRUCTOR for objects of type "string"
    }
}

请注意,要在代码中使用动态,您必须添加引用 Microsoft.CSharp

如果.NET框架的版本低于/低于/低于4.0且动态在该版本中未定义,则您必须使用对象而不是转换为整数类型,这很麻烦,所以我建议您至少使用.NET 4.0或更新版本,以便您可以使用动态而不是对象。 / p>

答案 15 :(得分:1)

目前还没有“好”的解决方案。但是,您可以显着缩小类型参数,以排除许多针对您的hypotetical“INumeric”约束的错误,正如Haacked所示。

static bool IntegerFunction&lt; T&gt;(T value)其中T:IComparable,IFormattable,IConvertible,IComparable&lt; T&gt;,IEquatable&lt; T&gt;,struct {...

答案 16 :(得分:1)

当我尝试为泛型类型重载运算符时,这个限制影响了我;因为没有“INumeric”约束,并且由于其他原因,stackoverflow上的好人很乐意提供,不能在泛型类型上定义操作。

我想要像

这样的东西
public struct Foo<T>
{
    public T Value{ get; private set; }

    public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
    {
        return new Foo<T> { Value = LHS.Value + RHS.Value; };
    }
}

我使用.net4动态运行时输入解决了这个问题。

public struct Foo<T>
{
    public T Value { get; private set; }

    public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
    {
        return new Foo<T> { Value = LHS.Value + (dynamic)RHS.Value };
    }
}

使用dynamic的两件事是

  1. 性能。所有价值类型都装箱。
  2. 运行时错误。你“击败”编译器,但失去了类型安全性。如果泛型类型没有定义运算符,则在执行期间将抛出异常。

答案 17 :(得分:1)

我会使用一个你可以处理的通用的......

/// <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;
}

答案 18 :(得分:1)

练习有什么意义?

正如人们已经指出的那样,你可以使用非泛型函数来获取最大项,编译器会自动为你转换较小的int。

static bool IntegerFunction(Int64 value) { }

如果您的功能处于性能关键路径(非常不可能,IMO),您可以为所有需要的功能提供重载。

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

答案 19 :(得分:0)

如果只想使用一种数字类型,则可以考虑使用using在C ++中创建类似于别名的内容。

因此,与其拥有非常通用的

T ComputeSomething<T>(T value1, T value2) where T : INumeric { ... }

您可以拥有

using MyNumType = System.Double;
T ComputeSomething<MyNumType>(MyNumType value1, MyNumType value2) { ... }

这可能会让您轻松地从double转到int或其他(如果需要),但是您将ComputeSomethingdouble和{{ 1}}。

但是为什么不将所有int替换为double呢?因为您的方法可能想使用int,而无论输入是double还是double。别名使您可以准确知道哪个变量使用 dynamic 类型。

答案 20 :(得分:0)

在类似的情况下,我需要处理数字类型和字符串。似乎有些奇怪,但您可以进行。

再次,就像许多人一样,我研究了约束,并提出了必须支持的一系列接口。但是,a)并非100%防水,b)任何新发现这种长约束条件的人都会立即感到困惑。

因此,我的方法是将我所有的逻辑都放到不受约束的通用方法中,而是将该通用方法设为私有。然后,我使用公共方法公开它,其中一个显式处理我想要处理的类型-在我看来,代码是干净且显式的,例如

select username, machine, to_char(logon_time,'HH:MM:SS')
from v$session 
where username='SYS'  <-- username

答案 21 :(得分:-5)

没有单个接口或基类都可以继承(不会被其他类继承),所以简单的答案是否定的。

我确实想知道为什么这是一个问题。你想在IntegerFunction类中做什么只能对整数做?

答案 22 :(得分:-9)

我认为你误解了仿制药。如果您尝试执行的操作仅适用于特定数据类型,那么您不会执行“通用”操作。

此外,由于您只想让函数处理int数据类型,因此您不需要为每个特定大小分别使用单独的函数。只需获取最大特定类型的参数,程序就可以自动将较小的数据类型向上转换。 (即在调用时传递Int16将自动转换为Int64。)

如果你根据传入函数的int的实际大小执行不同的操作,那么我认为你应该认真考虑甚至尝试做你正在做的事情。如果你不得不欺骗这种语言,你应该多考虑一下你想要完成什么而不是如何做你想做的事。

如果失败了,可以使用Object类型的参数,然后你必须检查参数的类型并采取适当的行动或抛出异常。