web-dev-qa-db-fra.com

Génériques - où T est un nombre?

J'essaie de trouver un moyen de créer une classe générique pour les types de nombres uniquement, pour faire des calculs.

Existe-t-il une interface commune pour tous les types de nombres (int, double, float ...) qui me manquent ???

Sinon, quelle sera la meilleure façon de créer une telle classe?

MISE À JOUR:

La principale chose que j'essaie de réaliser est de vérifier qui est le plus grand entre deux variables de type T.

55
CD..

Quelle version de .NET utilisez-vous? Si vous utilisez .NET 3.5, alors j'ai implémentation des opérateurs génériques in MiscUtil (gratuit, etc.).

Cela a des méthodes comme T Add<T>(T x, T y), et d'autres variantes pour l'arithmétique sur différents types (comme DateTime + TimeSpan).

De plus, cela fonctionne pour tous les opérateurs intégrés, levés et sur mesure, et met en cache le délégué pour les performances.

Quelques informations supplémentaires sur les raisons pour lesquelles cela est délicat sont ici .

Vous voudrez peut-être aussi savoir que dynamic (4.0) résout ce problème indirectement aussi - c.-à-d.

dynamic x = ..., y = ...
dynamic result = x + y; // does what you expect

Re le commentaire sur </> - vous n'avez pas réellement besoin d'opérateurs pour cela; vous avez juste besoin:

T x = ..., T y = ...
int c = Comparer<T>.Default.Compare(x,y);
if(c < 0) {
    // x < y
} else if (c > 0) { 
    // x > y
}
30
Marc Gravell

Il existe des interfaces pour certaines opérations sur les types de nombres, comme le IComparable<T>, IConvertible et IEquatable<T> interfaces. Vous pouvez spécifier cela pour obtenir une fonctionnalité spécifique:

public class MaxFinder<T> where T : IComparable<T> {

   public T FindMax(IEnumerable<T> items) {
      T result = default(T);
      bool first = true;
      foreach (T item in items) {
         if (first) {
            result = item;
            first = false;
         } else {
            if (item.CompareTo(result) > 0) {
               result = item;
            }
         }
      }
      return result;
   }

}

Vous pouvez utiliser des délégués pour développer une classe avec des opérations spécifiques au type:

public class Adder<T> {

   public delegate T AddDelegate(T item1, T item2);

   public T AddAll(IEnumerable<T> items, AddDelegate add) {
      T result = default(T);
      foreach (T item in items) {
         result = add(result, item);
      }
      return result;
   }

}

Usage:

Adder<int> adder = new Adder<int>();
int[] list = { 1, 2, 3 };
int sum = adder.AddAll(list, delegate(int x, int y) { return x + y; });

Vous pouvez également stocker des délégués dans la classe et disposer de différentes méthodes d'usine qui configurent des délégués pour un type de données spécifique. De cette façon, le code spécifique au type est uniquement dans les méthodes d'usine.

15
Guffa

Le plus proche que vous obtenez est struct, j'ai peur. Vous devrez effectuer des vérifications plus approfondies pour les types de numéros dans le code.

public class MyClass<T> where T : struct
(...)
8
EventHorizon

Vous ne pouvez pas le faire, car vous devez utiliser une seule interface pour les opérations arithmétiques. Il y a eu de nombreuses demandes sur Connect pour ajouter une interface IArithmetic à cet effet spécifique, mais jusqu'à présent, elles ont toutes été rejetées.

Vous pouvez en quelque sorte contourner cela en définissant une structure sans membres, qui implémente une interface "Calculatrice". Nous avons adopté cette approche dans une classe générique d'interpolation dans le Pluto Toolkit . Pour un exemple détaillé, nous avons une implémentation de calculatrice "vectorielle" ici , qui permet à notre interpolateur générique de travailler avec des vecteurs. Il existe des similaires pour les flotteurs, doubles, quaternions, etc.

8
Reed Copsey

Dans Framework BCL (bibliothèque de classes de base), de nombreuses fonctions numériques (telles que les fonctions dans System.Math) gèrent cela en ayant des surcharges pour chaque type numérique.

La classe Math statique dans la BCL contient des méthodes statiques, que vous pouvez appeler sans avoir à créer une instance de la classe. Vous pourriez faire de même dans votre classe. Par exemple, Math.Max ​​a 11 surcharges:

public static byte Max(byte val1, byte val2);
public static decimal Max(decimal val1, decimal val2);
public static double Max(double val1, double val2);
public static short Max(short val1, short val2);
public static int Max(int val1, int val2);
public static long Max(long val1, long val2);
public static sbyte Max(sbyte val1, sbyte val2);
public static float Max(float val1, float val2);
public static ushort Max(ushort val1, ushort val2);
public static uint Max(uint val1, uint val2);
public static ulong Max(ulong val1, ulong val2);
4
Robert Harvey
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GenericPratice1
{
    public delegate T Del<T>(T numone, T numtwo)where T:struct;
    class Class1
    {
        public T Addition<T>(T numone, T numtwo) where T:struct
        {
            return ((dynamic)numone + (dynamic)numtwo);
        }
        public T Substraction<T>(T numone, T numtwo) where T : struct
        {
            return ((dynamic)numone - (dynamic)numtwo);
        }
        public T Division<T>(T numone, T numtwo) where T : struct
        {
            return ((dynamic)numone / (dynamic)numtwo);
        }
        public T Multiplication<T>(T numone, T numtwo) where T : struct
        {
            return ((dynamic)numone * (dynamic)numtwo);
        }

        public Del<T> GetMethodInt<T>(int ch)  where T:struct
        {
            Console.WriteLine("Enter the NumberOne::");
            T numone =(T) Convert.ChangeType((object)(Console.ReadLine()), typeof(T));
            Console.WriteLine("Enter the NumberTwo::");
            T numtwo = (T)Convert.ChangeType((object)(Console.ReadLine()), typeof(T));
            T result = default(T);
            Class1 c = this;
            Del<T> deleg = null;
            switch (ch)
            {
                case 1:
                    deleg = c.Addition<T>;
                    result = deleg.Invoke(numone, numtwo);
                    break;
                case 2: deleg = c.Substraction<T>;
                    result = deleg.Invoke(numone, numtwo);
                    break;
                case 3: deleg = c.Division<T>;
                    result = deleg.Invoke(numone, numtwo);
                    break;
                case 4: deleg = c.Multiplication<T>;
                    result = deleg.Invoke(numone, numtwo);
                    break;
                default:
                    Console.WriteLine("Invalid entry");
                    break;
            }
            Console.WriteLine("Result is:: " + result);
            return deleg;
        }

    }
    class Calculator
    {
        public static void Main(string[] args)
        {
            Class1 cs = new Class1();
            Console.WriteLine("Enter the DataType choice:");
            Console.WriteLine("1 : Int\n2 : Float");
            int sel = Convert.ToInt32(Console.ReadLine());
            Console.WriteLine("Enter the choice::");
            Console.WriteLine("1 : Addition\n2 : Substraction\3 : Division\4 : Multiplication");
            int ch = Convert.ToInt32(Console.ReadLine());
            if (sel == 1)
            {
                cs.GetMethodInt<int>(ch);
            }
            else
            {
                cs.GetMethodInt<float>(ch);
            }

        }
    }
}
3
Tanmay Desai

Vous ne pouvez pas le faire uniquement lors de la compilation. Mais vous pourriez mettre plus de contraintes pour éliminer la plupart des "mauvais types" sur votre type numérique comme ci-dessous

classe yourclass <T> où T: IComparable, IFormattable, IConvertible, IComparabe <T>, IEquatable <T>, struct {... En fin de compte, vous devrez toujours vérifier au moment de l'exécution si votre type est acceptable à l'aide de object.GetType ( ) méthode.

Si l'on compare seulement, IComparable <T> seul fait l'affaire.

2
dmihailescu

Je ne pense pas que vous puissiez définir cela en utilisant une contrainte de type générique. Votre code pourrait vérifier en interne vos besoins, en utilisant éventuellement Double.Parse ou Double.TryParse pour déterminer s'il s'agit d'un nombre--ou si VB.NET n'est pas hors de question, vous pouvez utiliser la fonction IsNumeric ().

Edit: Vous pouvez ajouter une référence à Microsoft.VisualBasic.dll et appeler la fonction IsNumeric () à partir de c #

2
STW