Помощь с математическими операндами в классе (c #) - PullRequest
7 голосов
/ 14 апреля 2011
public class Racional<T>
{
    private T nominator;
    private T denominator;
    public T Nominator
    {
        get { return nominator; }
        set { nominator = value; }
    }
    public T Denominator
    {
        get { return denominator; }
        set { denominator = value; }
    }
    public Racional(T nominator, T denominator)
    {
        this.nominator = nominator;
        this.denominator = denominator;
    }
    public static Racional<int> operator *(Racional<int> a, Racional<int> b)
    {
        return ((int)(a.nominator + b.nominator, a.denominator + b.denominator));
    }
    public override string ToString()
    {
        return "(" + this.nominator + " " + this.denominator + ")";
    }
}

Меня интересует эта часть:

public static Racional<int> operator *(Racional<int> a, Racional<int> b)
{
    return ((int)(a.nominator + b.nominator,  a.denominator + b.denominator));
}

Что не так:

Одним из параметров бинарного оператора должен быть содержащий тип

Как я могу нормально кодировать эту часть для математических операций?

Ответы [ 3 ]

3 голосов
/ 14 апреля 2011

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

У меня есть несколько вопросов:

  1. Почему тип Rational должен быть универсальным?Рациональное число определено как число, которое может быть выражено как отношение / дробь двух целых чисел (где знаменатель не 0).Почему бы не сделать тип неуниверсальным и просто использовать int повсюду?Или вы намереваетесь использовать этот тип для других целочисленных типов, таких как long и BigInteger?В этом случае рассмотрите возможность использования чего-либо, подобного предложению Алиостада, если вам нужен какой-то механизм совместного использования кода.
  2. Почему вы хотите, чтобы произведение двух рациональных чисел было равно сумме их числителей над суммой ихзнаменатели?Это не имеет смысла для меня.

В любом случае, вы, похоже, хотите иметь возможность «в общем» добавить два экземпляра типа «добавляемый».К сожалению, в настоящее время нет никакого способа выразить ограничение «имеет подходящий оператор сложения» в C #.

Метод # 1: Одним из обходных путей для этого в C # 4 является использованиеdynamic введите желаемую семантику "виртуального оператора".

public static Racional<T> operator *(Racional<T> a, Racional<T> b)
{
    var nominatorSum = (dynamic)a.Nominator + b.Nominator;
    var denominatorSum = (dynamic)a.Denominator + b.Denominator;

    return new Racional<T>(nominatorSum, denominatorSum);
}

Оператор сгенерирует, если у типа нет подходящего оператора сложения.


Метод # 2: Другой (более эффективный) способиспользовать деревья выражений.

Сначала создайте и кэшируйте делегат, который может выполнить сложение, скомпилировав соответствующее выражение:

private readonly static Func<T, T, T> Adder;

static Racional()
{
    var firstOperand = Expression.Parameter(typeof(T), "x");
    var secondOperand = Expression.Parameter(typeof(T), "y");
    var body = Expression.Add(firstOperand, secondOperand);
     Adder = Expression.Lambda<Func<T, T, T>>
                (body, firstOperand, secondOperand).Compile();    
} 

(Статический конструктор выдает, если тип неУ вас нет подходящего оператора сложения.)

Затем используйте его в операторе:

public static Racional<T> operator *(Racional<T> a, Racional<T> b)
{
    var nominatorSum = Adder(a.Nominator, b.Nominator);
    var denominatorSum = Adder(a.Denominator, b.Denominator);
    return new Racional<T>(nominatorSum, denominatorSum);
}
2 голосов
/ 14 апреля 2011

Проблема в том, что вы определяете оператор для Racional<int> в классе Racional<T>. Это невозможно. Типы не совпадают, вы можете определить оператор только для Racional<T>.

Обобщения не могут выражать обобщение операторов, поскольку они определены только для определенных типов. Решение заключается в создании класса и наследовании от Racional<int>:

public class IntRacional : Racional<int>
{
    public static Racional<int> operator +(IntRacional a, IntRacional b)
    {
        return new Racional<int>()
        {
            Nominator = a.Nominator + b.Nominator,
            Denominator = a.Denominator + b.Denominator
        };
    }
}
1 голос
/ 14 апреля 2011

Чтобы решить вашу проблему, вам нужно предоставить функции преобразования из T в некоторый тип, где определено operator+, и наоборот.Предполагая, что Int64 достаточно велико в большинстве случаев, это можно сделать следующим образом:

public class Racional<T> 
{
    private T nominator;
    private T denominator;
    static Converter<T,Int64> T_to_Int64;
    static Converter<Int64,T> Int64_to_T;

    public static void InitConverters(Converter<T,Int64> t2int, Converter<Int64,T> int2t )
    {
        T_to_Int64 = t2int;
        Int64_to_T = int2t;
    }

    public T Nominator
    {
        get { return nominator; }
        set { nominator = value; }
    }
    public T Denominator
    {
        get { return denominator; }
        set { denominator = value; }
    }
    public Racional(T nominator, T denominator)
    {
        this.nominator = nominator;
        this.denominator = denominator;
    }
    public static Racional<T> operator *(Racional<T> a, Racional<T> b) 
    {
        return new Racional<T>(
            Int64_to_T(T_to_Int64(a.nominator) + T_to_Int64(b.nominator)),
            Int64_to_T(T_to_Int64(a.denominator) + T_to_Int64(b.denominator)));
    }

    // By the way, should this not be * instead of + ???
    //
    // public static Racional<T> operator *(Racional<T> a, Racional<T> b) 
    // {
    //    return new Racional<T>(
    //        Int64_to_T(T_to_Int64(a.nominator) * T_to_Int64(b.nominator)),
    //        Int64_to_T(T_to_Int64(a.denominator) * T_to_Int64(b.denominator)));
    // }



    public override string ToString()
    {
        return "(" + this.nominator + " " + this.denominator + ")";
    }
}

Конечно, это имеет тот недостаток, что вы должны обеспечить инициализацию этих преобразователей где-то в начале программы, есливыглядят так:

Racional<int>.InitConverters(x => (Int64)x, y => (int)y);

В реальной программе вы можете знать, какие возможные замены для T вы собираетесь использовать.Таким образом, можно предоставить эти 3 или 4 вызова в статическом конструкторе следующим образом:

    public static Racional()
    {
        Racional<int>.InitConverters(x => (Int64)x, y => (int)y);
        Racional<short>.InitConverters(x => (Int64)x, y => (short)y);
        Racional<Int64>.InitConverters(x => (Int64)x, y => (Int64)y);
    }

должно быть достаточно в большинстве случаев.Обратите внимание, что эта инициализация преобразователя повторяется для всех 3 типов снова 3 раза, повторно инициализируя функции преобразования снова несколько раз.На практике это не должно создавать никаких проблем.

...