Арифметические операции для классов с обобщенным типом - C#
Формулировка задачи:
Доброго времени!
Никак не могу разобраться в проблеме и прошу помощи и совета у авторитетных гуру!
Суть в следующем. Есть класс Matrix - поддерживает операции с матрицами и т.д. Класс объявлен как:
что позволяет хранить в матрице значения разных типов
Имеется также перегруженный оператор сложения
Но компилятор ругается на строку , дескать, невозможно совершить операцию + между типами Т и Т.
Отсюда вопрос:
1. Почему в данном случае не работает ограничение 2. Какой есть выход из ситуации - третий день бьюсь, но похоже отсутствие достаточного количества мозгов не позволяет ничего придумать
Задача дополняется наличием структуры типа Fraction (дробь), в которой также переопределены арифметические операторы.
При этом T должен поддерживать тип Fraction
Код дроби:
Но сейчас пока главное разобраться как реализовать арифметику для Т и Т
Class Matrix<T> where T: struct
/// <summary>
/// Сложение двух матриц
/// </summary>
/// <param name="M1">Матрица - первое слагаемое</param>
/// <param name="M2">Матрица - второе слагаемое</param>
/// <returns>Матрица - сумма</returns>
public static Matrix<T> operator + (Matrix<T> M1, Matrix<T> M2)
{
if (ReferenceEquals(M1, null) || ReferenceEquals(M2, null))
throw new InvalidOperationException(String.Format("[ID:{0},{1}] -> Не создан один из операндов операции!", M1.ID, M2.ID));
Matrix<T> ret_val = new Matrix<T>(M1.Width, M1.Height, null);
try
{
if (!M1.CanAdd(M2))
{
throw new ArgumentException(String.Format("[ID:{0},{1}] -> Невозможно выполнить указанную операцию!", M1.ID, M2.ID));
}
else
{
for (int r = 0; r < M1.Height; r++)
for (int c = 0; c < M1.Width; c++)
ret_val[r, c] = M1[r, c] + M2[r, c];
}
}
catch (ArgumentException aex) { Log.AddString(aex.Message); }
catch (Exception ex){ Log.AddString(ex.Message); }
// возврат
return ret_val;
}ret_val[r, c] = M1[r, c] + M2[r, c];
where T: struct
#region "Fraction"
/// <summary>
/// Структура представления рациональной дроби
/// nominator - числитель дроби
/// denominator - знаменатель дроби
/// reducable - Флаг немедленного сокращения дроби-результата выполнения арифметических операций
/// </summary>
public struct Fraction
{
private int nominator;
public int Nominator { get { return nominator; } }
private int denominator;
public int Denominator { get { return denominator; } }
private static bool reducable;
public static bool Reducable { get { return reducable; } set { reducable = value; } }
/// <summary>
/// Конструктор по целому значению
/// </summary>
/// <param name="Value">Целое число, которое будет числителем дроби (знаменатель=1)</param>
public Fraction(int Value)
{
nominator = Value;
denominator = 1;
}
/// <summary>
/// Конструктор по числителю и знаменателю
/// </summary>
/// <param name="Nom">Устанавливает числитель дроби</param>
/// <param name="Denom">Устанавливает знаменатель дроби</param>
public Fraction(int Nom, int Denom)
{
if (Denom == 0)
{
nominator = 0;
denominator = 1;
throw new ArgumentException("Знаменатель дроби не может быть нулевым!");
}
else
{
nominator = Nom;
denominator = Denom;
}
}
/// <summary>
/// Конструктор копирования дроби
/// </summary>
/// <param name="F">Копирует числитель и знаменатель из указанной дроби F</param>
public Fraction(Fraction F)
{
nominator = F.nominator;
denominator = F.denominator;
}
/// <summary>
/// Переопределение стандартного вывода дроби в виде строки
/// </summary>
/// <returns>Строка в формате n/d</returns>
public override string ToString()
{
return String.Format("{0}/{1}", nominator, denominator);
}
/// <summary>
/// Декомпозиция дроби
/// В результате декомпозиции дробь становиться правильной (целая часть отсекается и возвращается как результат
/// </summary>
/// <returns>Целая часть дроби</returns>
public int Decompose()
{
int ret_val = (nominator - nominator % denominator) / denominator;
nominator = nominator % denominator;
return ret_val;
}
/// <summary>
/// Сокращение дроби (деление числителя и знаменателя на НОД)
/// </summary>
public void Reduce()
{
for (int i = nominator > denominator ? nominator : denominator;
i >1; i--)
{
if (nominator % i == 0 && denominator % i == 0)
{
nominator /= i;
denominator /= i;
break;
}
}
}
/// <summary>
/// Проведение операции сложения двух дробей
/// </summary>
/// <param name="F1">Первое слагаемое</param>
/// <param name="F2">Второе слагаемое</param>
/// <returns>Результат сложения двух дробей (результат будет сокращен, если установлено свойство Reducable)</returns>
public static Fraction operator +(Fraction F1, Fraction F2)
{
Fraction ret_val = new Fraction();
if (F1.denominator == F2.denominator)
{
ret_val.denominator = F1.denominator;
ret_val.nominator = F1.nominator + F2.nominator;
}
else
{
ret_val.denominator = F1.denominator * F2.denominator;
ret_val.nominator = F1.nominator * F2.denominator + F2.nominator * F1.denominator;
}
// reduce
if(reducable) ret_val.Reduce();
// return
return ret_val;
}
/// <summary>
/// Проведение операции вычитания двух дробей
/// </summary>
/// <param name="F1">Уменьшаемое</param>
/// <param name="F2">Вычитаемое</param>
/// <returns>Результат вычитания двух дробей (результат будет сокращен, если установлено свойство Reducable)</returns>
public static Fraction operator -(Fraction F1, Fraction F2)
{
Fraction ret_val = new Fraction();
if (F1.denominator == F2.denominator)
{
ret_val.denominator = F1.denominator;
ret_val.nominator = F1.nominator - F2.nominator;
}
else
{
ret_val.denominator = F1.denominator * F2.denominator;
ret_val.nominator = F1.nominator * F2.denominator - F2.nominator * F1.denominator;
}
// reduce
if (reducable) ret_val.Reduce();
// return
return ret_val;
}
/// <summary>
/// Проведение операции умножения двух дробей
/// </summary>
/// <param name="F1">Первый множитель</param>
/// <param name="F2">Второй множитель</param>
/// <returns>Результат умножения двух дробей (результат будет сокращен, если установлено свойство Reducable)</returns>
public static Fraction operator *(Fraction F1, Fraction F2)
{
Fraction ret_val = new Fraction();
ret_val.denominator = F1.denominator * F2.denominator;
ret_val.nominator = F1.nominator * F2.nominator;
// reduce
if (reducable) ret_val.Reduce();
// return
return ret_val;
}
/// <summary>
/// Проведение операции деления двух дробей
/// </summary>
/// <param name="F1">Делимое/param>
/// <param name="F2">Делитель</param>
/// <returns>Результат деления двух дробей (результат будет сокращен, если установлено свойство Reducable)</returns>
public static Fraction operator /(Fraction F1, Fraction F2)
{
Fraction ret_val = new Fraction();
ret_val.nominator = F1.nominator * F2.denominator;
ret_val.denominator = F1.denominator * F2.nominator;
// reduce
if (reducable) ret_val.Reduce();
// return
return ret_val;
}
/// <summary>
/// Неявное преобразование целого числа в дробь
/// </summary>
/// <param name="Val">Целое число для преобразования</param>
/// <returns>Возвращаемая дробь, числителем которой является Val, а знаменателем - 1</returns>
public static implicit operator Fraction(int Val)
{
Fraction F = new Fraction(Val);
return F;
}
/// <summary>
/// Явное преобразование дроби в целое число
/// </summary>
/// <param name="F">Дробь для преобразования</param>
/// <returns>Возвращаемое значение - целая часть дроби</returns>
public static explicit operator int(Fraction F)
{
return F.Decompose();
}
/// <summary>
/// Явное преобразование дроби в число с плавающей точкой
/// </summary>
/// <param name="F">Дробь для преобразования</param>
/// <returns>Возвращаемое значение - вычисленная дробь в десятичном формате с плавающей точкой</returns>
public static explicit operator double(Fraction F)
{
double ret_val;
ret_val = (double)F.nominator / (double)F.denominator;
return ret_val;
}
}
#endregionРешение задачи: «Арифметические операции для классов с обобщенным типом»
textual
Листинг программы
private int nominator;
/// <summary>
/// Числитель
/// </summary>
public int Nominator { get { return nominator; } }
private int denominator;
/// <summary>
/// Знаменатель
/// </summary>
public int Denominator { get { return denominator == 0 ? 1 : denominator; } }