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));
}
出了什么问题:
二元运算符的一个参数必须是包含类型
如何对数学运算中的这部分进行规范编码?
答案 0 :(得分:3)
编译器错误解释了代码无法编译的原因。包含类型是泛型类型定义,并且这种类型的泛型类型构造不被视为相同类型。
我有几个问题:
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);
}
答案 1 :(得分:2)
这里的问题是您在班级Racional<int>
中为Racional<T>
定义了一个运算符。这是不可能的。 类型不一样,您只能为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
};
}
}
答案 2 :(得分:1)
要解决您的问题,您需要提供从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次,再次多次重新初始化转换函数。在实践中,这不应该有任何麻烦。