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 + ")";
}
}
I'm interested in this part :
public static Racional<int> operator *(Racional<int> a, Racional<int> b)
{
return ((int)(a.nominator + b.nominator, a.denominator + b.denominator));
}
What's wrong:
One of the parameters of a binary operator must be the containing type
How I can normaly code this part for mathematic operations?
%= Modulus AND assignment operator. It takes modulus using two operands and assigns the result to the left operand. C %= A is equivalent to C = C % A.
But here, we will understand only the Arithmetic Operator in the C programming language. Arithmetic Operator is used to performing mathematical operations such as addition, subtraction, multiplication, division, modulus, etc., on the given operands.
The reason your code doesn't compile is explained by the compiler error. The containing type is a generic type definition, and a generic type constructed from such a type is not considered to be the same type.
I have a few questions:
Rational
type be generic? A rational number is defined as a number that can be expressed as the quotient / fraction of two integers (where the denominator is not 0
). Why not make the type non-generic and simply use int
throughout? Or do you intend that the type be used for other integral types such as long
and BigInteger
? In that case, consider using something like Aliostad's suggestion if you want some code-sharing mechanism.In any case, you appear to want to be able to 'generically' add two instances of an 'addable' type. Unfortunately, there currently isn't any way to express a 'has a suitable addition operator' constraint in C#.
Method #1: One workaround for this in C# 4 is to use the dynamic
type to give you the desired "virtual operator" semantics.
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);
}
The operator will throw if the type doesn't have a suitable addition operator.
Method #2: Another (more efficient) way is to use expression-trees.
First, create and cache a delegate that can perform the addition by compiling the appropriate expression:
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();
}
(The static constructor will throw if the type doesn't have a suitable addition operator.)
Then employ it in the operator:
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);
}
The issue here is you are defining an operator for Racional<int>
in the class Racional<T>
. This is not possible. The types are not the same, you can only define operator for Racional<T>
.
Generics cannot express generalization of operators since they are defined only for a certain types. Solution is to create a class and inherit from 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
};
}
}
To solve your issue, you need to provide conversion functions from T
to some type where operator+
is defined and vice versa. Assuming Int64
is big enough in most cases, this can be done this way:
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 + ")";
}
}
Of course, this has the drawback that you must provide the initialization of those converters somewhere at the program start, should look like this:
Racional<int>.InitConverters(x => (Int64)x, y => (int)y);
In a real program, you may know which possible replacements for T
you are going to use. So one can provide those 3 or 4 calls in a static constructor like this:
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);
}
should be sufficient in most cases. Note that this converter initialization is repeated for all 3 types 3 times again, re-initializing the conversion functions multiple times again. In practice this should not make any trouble.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With