Reputation: 351
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?
Upvotes: 7
Views: 327
Reputation: 113442
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);
}
Upvotes: 3
Reputation: 20044
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.
Upvotes: 1
Reputation: 81680
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
};
}
}
Upvotes: 2