在泛型中实现算术?

时间:2012-06-08 15:04:58

标签: c# .net generics

是否可以在C#泛型中实现基本算术(至少是加法),就像with C++ templates一样?我已经尝试了一段时间来让它们起作用,但是C#不允许你多次声明相同的泛型类型,就像模板一样。

广泛的谷歌搜索没有提供答案。

编辑:谢谢,但我正在寻找的是一种在编译时进行算术运算的方法,在泛型类型中嵌入像教会数字这样的东西。这就是为什么我把我做过的文章联系起来的原因。算术泛型类型中,而不是算术泛型类型的实例上。

5 个答案:

答案 0 :(得分:7)

不幸的是,你不能对泛型类型使用算术运算

T Add(T a, T b)
{
    return a + b; // compiler error here
}

不适用于c#!

但是你可以创建自己的数字类型并重载运算符(算术,相等和implicitexplicit)。这使您可以以一种非常自然的方式使用它们。但是,您无法使用泛型创建继承层次结构。您将不得不使用非泛型基类或接口。

我刚用矢量类型做了。这是一个缩短的版本:

public class Vector
{
    private const double Eps = 1e-7;

    public Vector(double x, double y)
    {
        _x = x;
        _y = y;
    }

    private double _x;
    public double X
    {
        get { return _x; }
    }

    private double _y;
    public double Y
    {
        get { return _y; }
    }

    public static Vector operator +(Vector a, Vector b)
    {
        return new Vector(a._x + b._x, a._y + b._y);
    }

    public static Vector operator *(double d, Vector v)
    {
        return new Vector(d * v._x, d * v._y);
    }

    public static bool operator ==(Vector a, Vector b)
    {
        if (ReferenceEquals(a, null)) {
            return ReferenceEquals(b, null);
        }
        if (ReferenceEquals(b, null)) {
            return false;
        }
        return Math.Abs(a._x - b._x) < Eps && Math.Abs(a._y - b._y) < Eps;
    }

    public static bool operator !=(Vector a, Vector b)
    {
        return !(a == b);
    }

    public static implicit operator Vector(double[] point)
    {
        return new Vector(point[0], point[1]);
    }

    public static implicit operator Vector(PointF point)
    {
        return new Vector(point.X, point.Y);
    }

    public override int GetHashCode()
    {
        return _x.GetHashCode() ^ _y.GetHashCode();
    }

    public override bool Equals(object obj)
    {
        var other = obj as Vector;
        return other != null && Math.Abs(other._x - _x) < Eps && Math.Abs(other._y - _y) < Eps;
    }

    public override string ToString()
    {
        return String.Format("Vector({0:0.0000}, {1:0.0000})", _x, _y);
    }
}

答案 1 :(得分:4)

如果我的回答似乎不合适,请随时提供更多说明。

至少对C#语言中的运算符没有通用约束。正如Jon Skeet已经用Unconstrained Melody证明的那样,约束实际上可能在CLR本身中完全有效。

您可以使用约束做的最好的事情是提供接口/自定义类,以显示您需要的操作。您将无法提供原语(除非您也可能实现implicit运算符),但它至少可以让您为数学部分创建通用代码。

通用约束允许编译器根据最小公分母(由约束或缺少指定)推断可用成员。大多数情况下,泛型不受约束,因此只给出object语义。

<小时/> 或者,避免使用约束并使用dynamic临时存储泛型变量,然后做出假设(通过鸭子打字)它有相关的运算符:

class Program
{
    static void Main(string[] args)
    {
        var result = Add<int, long, float>(1, 2);
        Console.WriteLine(result); // 3
        Console.WriteLine(result.GetType().FullName); // System.Single
        Console.Read();
    }

    static T3 Add<T1, T2, T3>(T1 left, T2 right)
    {
        dynamic d1 = left;
        dynamic d2 = right;
        return (T3)(d1 + d2);
    }
}

这涉及到DLR并且会有一些性能开销(我没有确切的数字),特别是如果你打算计算性能至关重要的话。

<小时/> 我不确定你的意思是“多次声明相同的泛型类型”,这有效:

class Tuple<T1, T2> // etc.

var myTuple = new Tuple<int, int>(1, 2);

答案 2 :(得分:0)

朋友们,在C#中对此的直观回答是RTTI并在对象类中来回转换

enter code here

class MyMath
{
    public static T Add<T>(T a, T b) where T: struct
    {
        switch (typeof(T).Name)
        {
            case "Int32":
                return (T) (object)((int)(object)a + (int)(object)b);
            case "Double":
                return (T)(object)((double)(object)a + (double)(object)b);
            default:
                return default(T);
        }
    }
}

class Program
{
    public static int Main()
    {
        Console.WriteLine(MyMath.Add<double>(3.6, 2.12));
        return 0;
    }
}

答案 3 :(得分:0)

是的,可以使用动态类型变量来完成。

示例:

T Add(T value1, T value2)
{           
        dynamic a = value1;
        dynamic b = value2;
        return (a + b);
}

如需更多参考,请click here

答案 4 :(得分:0)

关于带有Enums和泛型的算术的相关答案。

如果您专门寻找按位运算符(例如 '&'、|^)与 Enums 作为通用约束一起使用;除了 IConveritible 之外,添加 System.Enum 作为约束是一个可行的解决方法。

public static bool Contains<T>(this T container, T Value) where T : Enum, IConvertible
{
    return (container.ToInt32(null) & Value.ToInt32(null)) != 0;
}

public static T Insert<T>(this ref T container, T Value) where T : struct, Enum, IConvertible
{
    // this is slow and a proof of concept, not recommend for frequent use
    container = (T)Enum.ToObject(typeof(T), container.ToInt32(null) | Value.ToInt32(null));

    return container;
}