Help with math operands in a class (C #)

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 am 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 the binary operator must be a containing type

How can I normalize this part for mathematical operations?

+7
source share
3 answers

The reason your code does not compile is due to a compiler error. A containing type is a definition of a generic type, and a generic type built from that type is not considered the same type.

I have a few questions:

  • Why should the Rational type be generic? A rational number is defined as a number that can be expressed as the quotient / fractional number of two integers (where the denominator is not 0 ). Why not make the type non-generic and just use int all along? Or do you assume that this type is used for other integral types such as long and BigInteger ? In this case, consider using something like Aliostad's suggestion if you want to use a code-sharing mechanism.
  • Why do you want the product of two rational numbers equal the sum of their numerators over the sum of their denominators? It makes no sense to me.

In any case, it seems to you that you want to be able to "generally" add two instances of the "addable" type. Unfortunately, there is currently no way to express a C expression with a suitable addition operator in C #.

Method # 1:. One way around this in C # 4 is to use the dynamic type to give you the desired semantics of the virtual operator.

 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 does not have a suitable addition operator.


Method # 2: Another (more efficient) way is to use expression trees.

First create and close a delegate that can perform the addition by compiling the corresponding 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 does not have a suitable addition operator.)

Then use it in the statement:

 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); } 
+3
source

The problem here is that you are defining an operator for Racional<int> in the Racional<T> class. It's impossible. Types do not match, you can define an operator for Racional<T> .

Generics cannot express a generalization of operators, since they are defined only for certain types. The 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 }; } } 
+2
source

To solve your problem, you need to provide conversion functions from T to some type where operator+ defined and vice versa. Assuming that Int64 is large enough in most cases, this can be done as follows:

 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 disadvantage that you must provide the initialization of these converters somewhere at the beginning of the program, should look like this:

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

In a real program, you can find out what possible replacements for T you are going to use. Thus, you can provide these 3 or 4 calls in a static constructor, for example:

  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 enough in most cases. Please note that this converter initialization is repeated for all 3 types 3 times again, reinitialization of the conversion functions several times. In practice, this should not create any problems.

+1
source

All Articles