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

どうしたの:

二項演算子のパラメーターの1つは、包含型でなければなりません

数学演算のためにこの部分を通常どのようにコーディングできますか?

4

3 に答える 3

3

コードがコンパイルされない理由は、コンパイラエラーによって説明されます。包含型はジェネリック型の定義であり、そのような型から構築されたジェネリック型は同じ型とは見なされません。

少し質問があります:

  1. Rationalタイプがジェネリックでなければならないのはなぜですか?有理数は、2つの整数の商/分数として表現できる数として定義されます(分母はそうではありません)。タイプを非ジェネリックにして、全体で使用してみませんか?または、その型をやなどの他の整数型に使用する予定ですか?その場合、コード共有メカニズムが必要な場合は、Aliostadの提案のようなものを使用することを検討してください。0intlongBigInteger
  2. 2つの有理数の積を、分母の合計よりも分子の合計に等しくしたいのはなぜですか?それは私には意味がありません。

いずれにせよ、「一般的に」「追加可能」タイプの2つのインスタンスを追加できるようにしたいと思われます。残念ながら、現在、C#で「適切な加算演算子を持っている」という制約を表現する方法はありません。

方法1: C#4でのこの回避策の1つは、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 に答える