帮助课堂上的数学操作数(c#)

时间:2011-04-14 11:54:11

标签: c# class operands

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

出了什么问题:

  

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

如何对数学运算中的这部分进行规范编码?

3 个答案:

答案 0 :(得分:3)

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

我有几个问题:

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

  3. 在任何情况下,您似乎都希望能够“通用”添加两个“可添加”类型的实例。不幸的是,目前没有任何方法可以在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次,再次多次重新初始化转换函数。在实践中,这不应该有任何麻烦。