7
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));
}

怎么了:

二元运算符的参数之一必须是包含类型

我如何才能正常编码这部分以进行数学运算?

4

3 回答 3

3

编译器错误解释了您的代码无法编译的原因。包含类型是泛型类型定义,从这种类型构造的泛型类型不被认为是同一类型。

我有几个问题:

  1. 为什么Rational类型必须是泛型的?有理数定义为可以表示为两个整数的商/分数的数字(其中分母不是0)。为什么不将类型设置为非泛型并简单地int在整个过程中使用?或者您是否打算将该类型用于其他整数类型,例如longBigInteger?在这种情况下,如果您想要一些代码共享机制,请考虑使用类似 Aliostad 的建议。
  2. 为什么你希望两个有理数的乘积等于它们的分子之和除以它们的分母之和?这对我来说没有意义。

在任何情况下,您似乎都希望能够“一般”添加两个“可添加”类型的实例。不幸的是,目前没有任何方法可以在 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);
}
于 2011-04-14T13:29:57.020 回答
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
        };
    }
}
于 2011-04-14T12:10:27.777 回答
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 次,再次重新初始化转换函数多次。在实践中,这应该不会造成任何麻烦。

于 2011-04-14T11:59:46.460 回答