Реализация арифметики в дженериках?

Возможно ли реализовать базовую арифметику (по крайней мере, добавление) в С# generics, как вы можете с шаблонами С++? Я пытался какое-то время получить их и работать, но С# не позволяет вам объявлять один и тот же общий тип несколько раз, как вы можете с помощью шаблонов.

Обширный поиск не дал ответа.

EDIT: Спасибо, но то, что я ищу, - это способ сделать арифметику во время компиляции, внедряя что-то вроде церковных цифр в типы дженериков. Вот почему я связал статью, которую я сделал. Арифметика в общих типах, а не арифметика для экземпляров родовых типов.

Ответ 1

К сожалению, вы не можете использовать арифметические операции над общими типами

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

не будет работать в С#!

Но вы можете создавать свои собственные числовые типы и перегружать операторы (арифметические, равенства и implicit, explicit). Это позволяет вам работать с ними вполне естественным образом. Однако вы не можете создать иерархию наследования с обобщениями. Вам придется использовать не универсальный базовый класс или интерфейс.

Я просто сделал это с векторным типом. Сокращенная версия здесь:

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

Ответ 2

Пожалуйста, не стесняйтесь предлагать больше разъяснений, если мой ответ кажется неуклюжим.

Не существует общих ограничений для операторов на языке С#, по крайней мере. Поскольку Джон Скит доказал, что 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);

Ответ 3

Друзья, интуитивный ответ на это в С# - 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;
    }
}

Ответ 4

да, это можно сделать, используя динамические переменные типа.

Пример:

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

для дополнительной справки нажмите здесь