【问题标题】:Help with mathematic operands in class (c#)帮助类中的数学操作数(c#)
【发布时间】:2011-04-14 11:54:10
【问题描述】:
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));
}

怎么了:

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

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

【问题讨论】:

  • 顺便说一句,这是一个家庭作业还是您打算在生产代码中使用它?查看msdn.microsoft.com/en-us/library/…
  • 您可能希望将类名更改为 Rational...
  • 为什么要创建一个开箱即用的由框架处理的类?如果这是家庭作业,这是一种可怕的做法。
  • @Roy:我认为它是英语以外的语言。至少他发布了他的实际代码,而不是冒着出现不相关的印刷错误的风险。
  • 在这里等一下,你是说 1/2 * 2/3 = 3/5 ?这段代码不仅不能编译,而且没有任何意义。你是说有理数具有任意类型作为分子和分母? 为什么这是通用的?有理数定义为两个整数之比。你可以有整数、字符串、异常等等。这段代码对我没有任何意义。你能解释一下吗?

标签: c# class operands


【解决方案1】:

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

我有几个问题:

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

【讨论】:

  • +1,非常有趣的是,这可以通过通用方式实现。我第一次知道表达式树有什么用处。
  • 好,为什么不在所有代码中使用 int:因为我的编程课有这样的实验室
【解决方案2】:

这里的问题是您在 Racional&lt;T&gt; 类中为 Racional&lt;int&gt; 定义一个运算符。这是不可能的。 类型不一样,只能为Racional&lt;T&gt;定义运算符。

泛型不能表达运算符的泛化,因为它们只为特定类型定义。解决方案是创建一个类并从Racional&lt;int&gt;继承:

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

【讨论】:

  • 这没有意义。你能详细说明一下吗?
  • Racional 和 Racional 在编译器眼中是两个不同的类。而且你不能在另一个类中重新定义一个类的行为(不包括扩展方法)。
【解决方案3】:

要解决您的问题,您需要提供从 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 次,再次重新初始化转换函数多次。实际上,这不会造成任何麻烦。

【讨论】:

  • 虚拟-1。猜错了!!不编译错误One of the parameters of a binary operator must be the containing type
  • @Aliostad:完全改变了我的答案。
猜你喜欢
  • 2011-11-12
  • 2011-09-14
  • 2011-07-06
  • 2011-08-07
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 2017-12-28
  • 2010-12-24
相关资源
最近更新 更多