帮助处理类(c#)中的数学操作数
本文关键字:操作数 处理 帮助 | 更新日期: 2023-09-27 17:50:15
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));
}
:
二元操作符的一个形参必须是包含类型
我怎么能正常编码这部分的数学运算?
你的代码不能编译的原因是编译器错误。包含类型是泛型类型定义,从这样的类型构造的泛型类型不被认为是相同的类型。
我有几个问题:
- 为什么
Rational
类型必须是通用的?有理数是,定义为可表示为两个整数的商/分数(其中分母不是0
)的数。为什么不使类型非通用和简单地使用int
自始至终?或者您打算将该类型用于其他整型,如long
和BigInteger
?在这种情况下,如果你想要一些代码共享机制,可以考虑使用Aliostad的建议。 - 为什么你想要两个有理数的乘积等于它们的分子和除以它们的分母和?这对我来说没有意义。
在任何情况下,您似乎都希望能够"一般地"添加两个"可添加"类型的实例。不幸的是,目前在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);
}
这里的问题是您在类Racional<T>
中定义Racional<int>
的操作符。这是不可能的。类型不一样,只能为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
};
}
}
要解决您的问题,您需要提供从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次,将转换函数重新初始化多次。在实践中,这应该不会造成任何麻烦。