2011-04-14 69 views
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 + ")"; 
    } 
} 

我感興趣的是這一部分:幫助在課堂上的數學操作數(C#)

public static Racional<int> operator *(Racional<int> a, Racional<int> b) 
{ 
    return ((int)(a.nominator + b.nominator, a.denominator + b.denominator)); 
} 

有什麼不對:

一個二元運算符的參數必須是包含類型

如何正常地編寫數學運算的這部分?

+0

BTW,這是一門功課,或者你打算在生產代碼中使用它?檢出http://msdn.microsoft.com/en-us/library/microsoft.solverfoundation.common.rational%28v=vs.93%29.aspx – 2011-04-14 12:04:56

+0

您可能希望將類名更改爲Rational ... – 2011-04-14 12:05:30

+0

爲什麼你要創建一個由框架處理的類?如果這是家庭作業,這是一個可怕的做法。 – 2011-04-14 12:06:42

回答

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

+1,非常有趣的是,這是可以通用的方式。第一次我得到了樹木表達好處的概念。 – 2011-04-14 14:12:37

+0

好,爲什麼不在所有的代碼int:因爲我有這樣的實驗室爲我的編程課 – user707895 2011-04-14 17:47:21

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個呼叫:

​​

應該是在大多數情況下足夠了。請注意,此轉換器初始化會再次重複3次,再次重新初始化轉換函數。在實踐中,這不應該有任何麻煩。

+0

虛擬-1。一個不好的猜測!不會以錯誤編譯'二元運算符的其中一個參數必須是包含類型' – Aliostad 2011-04-14 12:03:12

+0

@Aliostad:完全改變了我的答案。 – 2011-04-14 12:55:39

2

這裏的問題是您正在定義類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 
     }; 
    } 
} 
+0

這沒有意義。你能詳細說明嗎? – 2011-04-14 12:12:56

+0

在編譯器的眼中,種族和種族是兩種不同的類別。而且你不能重新定義另一個類的行爲(不包括擴展方法)。 – SWeko 2011-04-14 12:27:50