泛型 - 其中T是数字?

时间:2009-08-12 18:30:40

标签: c# generics numbers

我正试图想办法为数字类型创建一个通用类,用于进行一些计算。

我缺少所有数字类型(int,double,float ...)的通用接口吗?

如果没有,创建这样一个班级的最佳方法是什么?

更新

我想要实现的主要是检查两个类型为T的变量之间的对象是谁。

8 个答案:

答案 0 :(得分:29)

您使用的是哪个版本的.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

重新评论< / > - 您实际上并不是需要运营商;你只需要:

T x = ..., T y = ...
int c = Comparer<T>.Default.Compare(x,y);
if(c < 0) {
    // x < y
} else if (c > 0) { 
    // x > y
}

答案 1 :(得分:15)

数字类型的某些操作有接口,如IComparable<T>IConvertibleIEquatable<T>接口。您可以指定获取特定功能:

public class MaxFinder<T> where T : IComparable<T> {

   public T FindMax(IEnumerable<T> items) {
      T result = default(T);
      bool first = true;
      foreach (T item in items) {
         if (first) {
            result = item;
            first = false;
         } else {
            if (item.CompareTo(result) > 0) {
               result = item;
            }
         }
      }
      return result;
   }

}

您可以使用委托来扩展具有特定类型操作的类:

public class Adder<T> {

   public delegate T AddDelegate(T item1, T item2);

   public T AddAll(IEnumerable<T> items, AddDelegate add) {
      T result = default(T);
      foreach (T item in items) {
         result = add(result, item);
      }
      return result;
   }

}

用法:

Adder<int> adder = new Adder<int>();
int[] list = { 1, 2, 3 };
int sum = adder.AddAll(list, delegate(int x, int y) { return x + y; });

您还可以在类中存储委托,并具有为特定数据类型设置委托的不同工厂方法。这样,特定于类型的代码仅在工厂方法中。

答案 2 :(得分:8)

最接近的是结构我很害怕。您必须对代码中的数字类型进行更广泛的检查。

public class MyClass<T> where T : struct
(...)

答案 3 :(得分:8)

您不能这样做,因为您必须使用单个接口进行算术运算。 Connect上有很多请求为这个特定的目的添加一个IArithmetic接口,但到目前为止它们都被拒绝了。

你可以通过定义一个没有成员的结构来解决这个问题,这个结构实现了一个“计算器”接口。我们在Pluto Toolkit中的插值泛型类中采用了这种方法。有关详细示例,我们有一个“向量”计算器实现here,它允许我们的通用插值器使用向量。浮游物,双打,四元数等都有类似的颜色。

答案 4 :(得分:4)

在Framework BCL(基类库)中,许多数字函数(例如System.Math中的函数)通过​​为每个数字类型设置重载来处理此问题。

BCL中的静态Math类包含静态方法,您可以调用它们而无需创建类的实例。你可以在班上做同样的事情。例如,Math.Max有11个重载:

public static byte Max(byte val1, byte val2);
public static decimal Max(decimal val1, decimal val2);
public static double Max(double val1, double val2);
public static short Max(short val1, short val2);
public static int Max(int val1, int val2);
public static long Max(long val1, long val2);
public static sbyte Max(sbyte val1, sbyte val2);
public static float Max(float val1, float val2);
public static ushort Max(ushort val1, ushort val2);
public static uint Max(uint val1, uint val2);
public static ulong Max(ulong val1, ulong val2);

答案 5 :(得分:3)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GenericPratice1
{
    public delegate T Del<T>(T numone, T numtwo)where T:struct;
    class Class1
    {
        public T Addition<T>(T numone, T numtwo) where T:struct
        {
            return ((dynamic)numone + (dynamic)numtwo);
        }
        public T Substraction<T>(T numone, T numtwo) where T : struct
        {
            return ((dynamic)numone - (dynamic)numtwo);
        }
        public T Division<T>(T numone, T numtwo) where T : struct
        {
            return ((dynamic)numone / (dynamic)numtwo);
        }
        public T Multiplication<T>(T numone, T numtwo) where T : struct
        {
            return ((dynamic)numone * (dynamic)numtwo);
        }

        public Del<T> GetMethodInt<T>(int ch)  where T:struct
        {
            Console.WriteLine("Enter the NumberOne::");
            T numone =(T) Convert.ChangeType((object)(Console.ReadLine()), typeof(T));
            Console.WriteLine("Enter the NumberTwo::");
            T numtwo = (T)Convert.ChangeType((object)(Console.ReadLine()), typeof(T));
            T result = default(T);
            Class1 c = this;
            Del<T> deleg = null;
            switch (ch)
            {
                case 1:
                    deleg = c.Addition<T>;
                    result = deleg.Invoke(numone, numtwo);
                    break;
                case 2: deleg = c.Substraction<T>;
                    result = deleg.Invoke(numone, numtwo);
                    break;
                case 3: deleg = c.Division<T>;
                    result = deleg.Invoke(numone, numtwo);
                    break;
                case 4: deleg = c.Multiplication<T>;
                    result = deleg.Invoke(numone, numtwo);
                    break;
                default:
                    Console.WriteLine("Invalid entry");
                    break;
            }
            Console.WriteLine("Result is:: " + result);
            return deleg;
        }

    }
    class Calculator
    {
        public static void Main(string[] args)
        {
            Class1 cs = new Class1();
            Console.WriteLine("Enter the DataType choice:");
            Console.WriteLine("1 : Int\n2 : Float");
            int sel = Convert.ToInt32(Console.ReadLine());
            Console.WriteLine("Enter the choice::");
            Console.WriteLine("1 : Addition\n2 : Substraction\3 : Division\4 : Multiplication");
            int ch = Convert.ToInt32(Console.ReadLine());
            if (sel == 1)
            {
                cs.GetMethodInt<int>(ch);
            }
            else
            {
                cs.GetMethodInt<float>(ch);
            }

        }
    }
}

答案 6 :(得分:2)

我不相信你可以使用泛型类型约束来定义它。您的代码可以在内部检查您的需求,可能使用Double.Parse或Double.TryParse来确定它是否为数字 - 或者如果VB.NET不是不可能的话那么您可以使用IsNumeric()函数

编辑:您可以添加对Microsoft.VisualBasic.dll的引用,并从c#中调用IsNumeric()函数

答案 7 :(得分:2)

您不能仅在编译时执行此操作。 但你可以设置更多限制来清除数字类型中的大多数“坏类型”,如下面的

class yourclass&lt; T&gt;其中T:IComparable,IFormattable,IConvertible,IComparabe&lt; T&gt;,IEquatable&lt; T&gt;,struct {... 最后,你仍然需要在运行时检查你的类型是否可以使用object.GetType()方法。

如果仅比较,则IComparable&lt; T&gt;独自完成这个伎俩。