我正试图想办法为数字类型创建一个通用类,用于进行一些计算。
我缺少所有数字类型(int,double,float ...)的通用接口吗?
如果没有,创建这样一个班级的最佳方法是什么?
更新
我想要实现的主要是检查两个类型为T的变量之间的对象是谁。
答案 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>
,IConvertible
和IEquatable<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;独自完成这个伎俩。