16

C++ テンプレートでできるように、C# ジェネリックで基本的な算術演算 (少なくとも足し算) を実装することは可能ですか? しばらくの間、それらを起動して機能させようとしましたが、C# では、テンプレートのように同じジェネリック型を複数回宣言することはできません。

大規模なグーグル検索では答えが得られませんでした。

編集: ありがとう、しかし私が探しているのは、コンパイル時に算術演算を行う方法であり、教会の数字のようなものをジェネリック型に埋め込みます。そのため、私が行った記事をリンクしました。ジェネリック型のインスタンスでの算術ではなく、ジェネリック型で算術。

4

6 に答える 6

7

残念ながら、ジェネリック型で算術演算を使用することはできません

T Add(T a, T b)
{
    return a + b; // compiler error here
}

C# では動作しません!

ただし、独自の数値型を作成して、演算子 (算術、等値、およびimplicit, explicit) をオーバーロードできます。これにより、非常に自然な方法でそれらを操作できます。ただし、ジェネリックを使用して継承階層を作成することはできません。非ジェネリックの基本クラスまたはインターフェイスを使用する必要があります。

ベクトル型でやっただけです。短縮版はこちら:

public class Vector
{
    private const double Eps = 1e-7;

    public Vector(double x, double y)
    {
        _x = x;
        _y = y;
    }

    private double _x;
    public double X
    {
        get { return _x; }
    }

    private double _y;
    public double Y
    {
        get { return _y; }
    }

    public static Vector operator +(Vector a, Vector b)
    {
        return new Vector(a._x + b._x, a._y + b._y);
    }

    public static Vector operator *(double d, Vector v)
    {
        return new Vector(d * v._x, d * v._y);
    }

    public static bool operator ==(Vector a, Vector b)
    {
        if (ReferenceEquals(a, null)) {
            return ReferenceEquals(b, null);
        }
        if (ReferenceEquals(b, null)) {
            return false;
        }
        return Math.Abs(a._x - b._x) < Eps && Math.Abs(a._y - b._y) < Eps;
    }

    public static bool operator !=(Vector a, Vector b)
    {
        return !(a == b);
    }

    public static implicit operator Vector(double[] point)
    {
        return new Vector(point[0], point[1]);
    }

    public static implicit operator Vector(PointF point)
    {
        return new Vector(point.X, point.Y);
    }

    public override int GetHashCode()
    {
        return _x.GetHashCode() ^ _y.GetHashCode();
    }

    public override bool Equals(object obj)
    {
        var other = obj as Vector;
        return other != null && Math.Abs(other._x - _x) < Eps && Math.Abs(other._y - _y) < Eps;
    }

    public override string ToString()
    {
        return String.Format("Vector({0:0.0000}, {1:0.0000})", _x, _y);
    }
}
于 2012-06-08T15:16:54.747 に答える
5

私の答えがうまくいかないように思われる場合は、お気軽にもっと明確にしてください。

少なくとも、C# 言語の演算子には一般的な制約はありません。Jon Skeet がUnconstrained Melodyで証明したように、制約は実際には CLR 自体で完全に有効である可能性があります。

制約でできる最善のことは、必要なアクションを公開するインターフェース/カスタム クラスを提供することです。プリミティブを提供することはできませんが (implicitおそらく演算子も実装しない限り)、少なくとも数学部分の汎用コードを作成できます。

ジェネリック制約を使用すると、コンパイラは最小公分母 (制約または不足によって指定) に基づいて使用可能なメンバーを推測できます。ほとんどの場合、ジェネリックは制約がないため、objectセマンティクスのみが提供されます。


または、制約の使用を避け、ジェネリック変数を一時dynamicに格納するために使用してから、関連する演算子があることを (ダックタイピングによって) 仮定します。

class Program
{
    static void Main(string[] args)
    {
        var result = Add<int, long, float>(1, 2);
        Console.WriteLine(result); // 3
        Console.WriteLine(result.GetType().FullName); // System.Single
        Console.Read();
    }

    static T3 Add<T1, T2, T3>(T1 left, T2 right)
    {
        dynamic d1 = left;
        dynamic d2 = right;
        return (T3)(d1 + d2);
    }
}

これには DLR が関係し、特にパフォーマンスが重要な計算を行う場合は、パフォーマンスのオーバーヘッドが発生します (正確な数値はわかりません)。


「同じジェネリック型を複数回宣言する」という意味がわかりませんが、これは機能します。

class Tuple<T1, T2> // etc.

var myTuple = new Tuple<int, int>(1, 2);
于 2012-06-08T15:07:09.207 に答える
4

友よ、これに対する C# での直感的な答えは、RTTI であり、オブジェクト クラスから前後にキャストすることです。

enter code here

class MyMath
{
    public static T Add<T>(T a, T b) where T: struct
    {
        switch (typeof(T).Name)
        {
            case "Int32":
                return (T) (object)((int)(object)a + (int)(object)b);
            case "Double":
                return (T)(object)((double)(object)a + (double)(object)b);
            default:
                return default(T);
        }
    }
}

class Program
{
    public static int Main()
    {
        Console.WriteLine(MyMath.Add<double>(3.6, 2.12));
        return 0;
    }
}
于 2014-10-17T06:51:15.953 に答える
4

はい、動的型変数を使用して実行できます。

例:

T Add(T value1, T value2)
{           
        dynamic a = value1;
        dynamic b = value2;
        return (a + b);
}

詳細については、ここをクリックしてください

于 2017-03-07T11:02:45.473 に答える
0

とジェネリクスを使用した算術Enumsに関して、接線方向に関連する回答。

一般的な制約として使用するために|、「&」などのビット単位の演算子を特に探している場合。に加えて制約として追加することは、実行可能な回避策です。^EnumsIConveritibleSystem.Enum

public static bool Contains<T>(this T container, T Value) where T : Enum, IConvertible
{
    return (container.ToInt32(null) & Value.ToInt32(null)) != 0;
}

public static T Insert<T>(this ref T container, T Value) where T : struct, Enum, IConvertible
{
    // this is slow and a proof of concept, not recommend for frequent use
    container = (T)Enum.ToObject(typeof(T), container.ToInt32(null) | Value.ToInt32(null));

    return container;
}
于 2021-08-09T07:44:05.473 に答える