Подтвердить что ты не робот

Дженерики - где Т - число?

Я пытаюсь понять способ создания универсального класса только для числовых типов, для выполнения некоторых вычислений.

Есть ли общий интерфейс для всех типов номеров (int, double, float...), которые мне не хватает?

Если нет, каким будет лучший способ создания такого класса?

UPDATE:

Главное, что я пытаюсь достичь, - это проверить, кто больше между двумя переменными типа T.

4b9b3361

Ответ 1

Какую версию .NET вы используете? Если вы используете .NET 3.5, то у меня есть реализация общих операторов в MiscUtil (бесплатно и т.д.).

У этого метода есть методы типа T Add<T>(T x, T y) и другие варианты арифметики для разных типов (например, DateTime + TimeSpan).

Кроме того, это работает для всех встроенных, снятых и сделанных на заказ операторов и кэширует делегата для выполнения.

Некоторые дополнительные сведения о том, почему это сложно, здесь.

Вы также можете знать, что метод dynamic (4.0) также разрешает эту проблему косвенно - т.е.

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

В комментариях к </> - на самом деле вам не нужны операторы; вам просто нужно:

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

Ответ 2

Существуют интерфейсы для некоторых операций над типами номеров, например, интерфейсы IComparable<T>, IConvertible и IEquatable<T>. Вы можете указать это, чтобы получить определенную функциональность:

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

}

Вы можете использовать делегаты для расширения класса с конкретными типами операций:

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

}

Использование:

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

Вы также можете хранить делегаты в классе и иметь разные методы factory, которые устанавливают делегаты для определенного типа данных. Таким образом, код типа специфичен только в factory методах.

Ответ 3

Вы не можете этого сделать, так как вам придется использовать один интерфейс для арифметических операций. Было много запросов на Connect, чтобы добавить IArithmetic интерфейс для этой конкретной цели, но до сих пор все они были отклонены.

Вы можете сортировать работу вокруг этого, определяя структуру без элементов, которая реализует интерфейс "Калькулятор". Мы использовали этот подход в родовом классе интерполяции в Pluto Toolkit. Для детального примера у нас есть "векторная" реализация калькулятора здесь, которая позволяет нашему родовому интерполятору работать с векторами. Существуют аналогичные для поплавков, удвоений, кватернионов и т.д.

Ответ 4

Ближе всего вы получаете, я боюсь. Вам нужно будет выполнить более обширные проверки типов номеров в коде.

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

Ответ 5

В Framework BCL (библиотека базового класса) многие числовые функции (такие как функции в System.Math) справляются с этим, имея перегрузки для каждого числового типа.

Статический класс Math в BCL содержит статические методы, которые вы можете вызывать, не создавая экземпляр класса. Вы можете сделать то же самое в своем классе. Например, Math.Max ​​имеет 11 перегрузок:

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

Ответ 6

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

        }
    }
}

Ответ 7

Я не верю, что вы можете определить это, используя ограничение общего типа. Ваш код может внутренне проверить ваши требования, возможно, используя Double.Parse или Double.TryParse, чтобы определить, является ли это числом - , или если VB.NET не может быть и речи, тогда вы можете использовать функцию IsNumeric().

Изменить: Вы можете добавить ссылку на Microsoft.VisualBasic.dll и вызвать функцию IsNumeric() из С#

Ответ 8

Вы не можете сделать это только во время компиляции. Но вы можете добавить больше ограничений для отсечения большинства "плохих типов" на ваш числовой тип, например ниже

класс yourclass <T> , где T: IComparable, IFormattable, IConvertible, IComparabe <T> , IEquatable <T> , struct {... В конце вам все равно придется проверять во время выполнения, если ваш тип допустим с использованием метода object.GetType().

Если сравнивать только, то IComparable <T> один делает трюк.