Есть ли ограничение, которое ограничивает мой общий метод численными типами?

Может ли кто-нибудь сказать мне, есть ли способ с генериками ограничить аргумент типового типа T только:

  • Int16
  • Int32
  • Int64
  • UInt16
  • UInt32
  • UInt64

Я знаю ключевое слово where, но не могу найти интерфейс для только этих типов,

Что-то вроде:

static bool IntegerFunction<T>(T value) where T : INumeric 

Ответ 1

Хейлсберг рассказал о причинах не реализования функции в интервью с Брюсом Эккелем.

Я должен признать, однако, что я не знаю, как он считает, что его предлагаемое решение будет работать. Его предложение - отложить арифметические операции до какого-то другого родового класса (прочитайте интервью!). Как это помогает? ИМХО, не так много.

Ответ 2

Учитывая популярность этого вопроса и интерес к такой функции, я с удивлением вижу, что ответа T4 пока нет.

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

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

Для этого:

  • Создайте новый файл текстового шаблона с именем GenericNumberMethodTemplate.tt.
  • Удалите автоматически сгенерированный код (вы сохраните большую часть его, но некоторые из них не нужны).
  • Добавьте следующий фрагмент:
<#@ template language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>

<# Type[] types = new[] {
    typeof(Int16), typeof(Int32), typeof(Int64),
    typeof(UInt16), typeof(UInt32), typeof(UInt64)
    };
#>

using System;
public static class MaxMath {
    <# foreach (var type in types) { 
    #>
        public static <#= type.Name #> Max (<#= type.Name #> val1, <#= type.Name #> val2) {
            return val1 > val2 ? val1 : val2;
        }
    <#
    } #>
}

Это. Теперь все готово.

Сохранение этого файла автоматически скомпилирует его в этот исходный файл:

using System;
public static class MaxMath {
    public static Int16 Max (Int16 val1, Int16 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static Int32 Max (Int32 val1, Int32 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static Int64 Max (Int64 val1, Int64 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static UInt16 Max (UInt16 val1, UInt16 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static UInt32 Max (UInt32 val1, UInt32 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static UInt64 Max (UInt64 val1, UInt64 val2) {
        return val1 > val2 ? val1 : val2;
    }
}

В вашем main методе вы можете убедиться, что у вас есть уверенность в компиляции:

namespace TTTTTest
{
    class Program
    {
        static void Main(string[] args)
        {
            long val1 = 5L;
            long val2 = 10L;
            Console.WriteLine(MaxMath.Max(val1, val2));
            Console.Read();
        }
    }
}

enter image description here

Я опережу одно замечание: нет, это не является нарушением принципа СУХОЙ. Принцип DRY заключается в том, чтобы люди не дублировали код в нескольких местах, что затрудняло бы поддержание приложения.

Это совсем не так: если вы хотите изменить, вы можете просто изменить шаблон (один источник для всего вашего поколения!), И это было сделано.

Чтобы использовать его с вашими собственными определениями, добавьте декларацию пространства имен (убедитесь, что она та же, что и та, которая будет определять вашу собственную реализацию), в ваш сгенерированный код и пометьте класс как partial. Затем добавьте эти строки в файл шаблона, чтобы он был включен в возможную компиляцию:

<#@ import namespace="TheNameSpaceYouWillUse" #>
<#@ assembly name="$(TargetPath)" #>

Позвольте быть честным: Это довольно круто.

Отказ от ответственности: на этот образец в значительной степени повлияли Metaprogramming в.NET Кевином Хаззардом и Джейсоном Боком, Manning Publications.

Ответ 3

Для этого нет ограничений. Это реальная проблема для тех, кто хочет использовать дженерики для числовых вычислений.

Я пойду дальше и скажу, что нам нужно

static bool GenericFunction<T>(T value) 
    where T : operators( +, -, /, * )

Или даже

static bool GenericFunction<T>(T value) 
    where T : Add, Subtract

К сожалению, у вас есть только интерфейсы, базовые классы и ключевые слова struct (должен быть тип значения), class (должен быть ссылочным типом) и new() (должен иметь конструктор по умолчанию)

Вы можете обернуть число в чем-то еще (похожее на INullable<T>), например здесь, на codeproject.


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

Ответ 4

Обходное решение с использованием политик:

interface INumericPolicy<T>
{
    T Zero();
    T Add(T a, T b);
    // add more functions here, such as multiplication etc.
}

struct NumericPolicies:
    INumericPolicy<int>,
    INumericPolicy<long>
    // add more INumericPolicy<> for different numeric types.
{
    int INumericPolicy<int>.Zero() { return 0; }
    long INumericPolicy<long>.Zero() { return 0; }
    int INumericPolicy<int>.Add(int a, int b) { return a + b; }
    long INumericPolicy<long>.Add(long a, long b) { return a + b; }
    // implement all functions from INumericPolicy<> interfaces.

    public static NumericPolicies Instance = new NumericPolicies();
}

Алгоритмы:

static class Algorithms
{
    public static T Sum<P, T>(this P p, params T[] a)
        where P: INumericPolicy<T>
    {
        var r = p.Zero();
        foreach(var i in a)
        {
            r = p.Add(r, i);
        }
        return r;
    }

}

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

int i = NumericPolicies.Instance.Sum(1, 2, 3, 4, 5);
long l = NumericPolicies.Instance.Sum(1L, 2, 3, 4, 5);
NumericPolicies.Instance.Sum("www", "") // compile-time error.

Решение является безопасным для компиляции. CityLizard Framework предоставляет скомпилированную версию для.NET 4.0. Файл - lib/NETFramework4.0/CityLizard.Policy.dll.

Он также доступен в Nuget: https://www.nuget.org/packages/CityLizard/. См. Структуру CityLizard.Policy.I.

Ответ 5

Этот вопрос представляет собой немного часто задаваемых вопросов, поэтому я размещаю это как wiki (так как раньше я писал аналогично, но это более старый); в любом случае...

Какую версию .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

Ответ 6

К сожалению, вы можете указать struct в предложении where в этом экземпляре. Кажется странным, что вы не можете указать Int16, Int32 и т.д., Но я уверен, что существует некоторая глубокая причина внедрения, лежащая в основе решения не допускать типы значений в предложении where.

Я предполагаю, что единственным решением является выполнение проверки времени выполнения, которая, к сожалению, предотвращает проблему, возникающую во время компиляции. Это будет похоже на: -

static bool IntegerFunction<T>(T value) where T : struct {
  if (typeof(T) != typeof(Int16)  &&
      typeof(T) != typeof(Int32)  &&
      typeof(T) != typeof(Int64)  &&
      typeof(T) != typeof(UInt16) &&
      typeof(T) != typeof(UInt32) &&
      typeof(T) != typeof(UInt64)) {
    throw new ArgumentException(
      string.Format("Type '{0}' is not valid.", typeof(T).ToString()));
  }

  // Rest of code...
}

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

Я бы также рассмотрел возможные последствия для этой реализации, возможно, там был более быстрый выход.

Ответ 7

Возможно, самое близкое, что вы можете сделать, это

static bool IntegerFunction<T>(T value) where T: struct

Не уверен, что вы могли бы сделать следующее

static bool IntegerFunction<T>(T value) where T: struct, IComparable
, IFormattable, IConvertible, IComparable<T>, IEquatable<T>

Для чего-то такого специфического, почему бы не просто перегрузить для каждого типа, список будет настолько коротким и, возможно, будет иметь меньший объем памяти.

Ответ 8

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

    class Something<TCell>
    {
        internal static TCell Sum(TCell first, TCell second)
        {
            if (typeof(TCell) == typeof(int))
                return (TCell)((object)(((int)((object)first)) + ((int)((object)second))));

            if (typeof(TCell) == typeof(double))
                return (TCell)((object)(((double)((object)first)) + ((double)((object)second))));

            return second;
        }
    }

Обратите внимание, что типыof вычисляются во время компиляции, поэтому операторы if будут удалены компилятором. Компилятор также удаляет ложные нажатия. Итак, что-то разрешило в компиляторе

        internal static int Sum(int first, int second)
        {
            return first + second;
        }

Ответ 9

Я создал небольшую библиотечную функциональность для решения этих проблем:

Вместо:

public T DifficultCalculation<T>(T a, T b)
{
    T result = a * b + a; // <== WILL NOT COMPILE!
    return result;
}
Console.WriteLine(DifficultCalculation(2, 3)); // Should result in 8.

Вы можете написать:

public T DifficultCalculation<T>(Number<T> a, Number<T> b)
{
    Number<T> result = a * b + a;
    return (T)result;
}
Console.WriteLine(DifficultCalculation(2, 3)); // Results in 8.

Здесь вы можете найти исходный код: https://codereview.stackexchange.com/questions/26022/improvement-requested-for-generic-calculator-and-generic-number

Ответ 10

Мне было интересно, как samjudson, почему только целые числа? и если это так, вам может понадобиться создать вспомогательный класс или что-то подобное для хранения всех типов, которые вы хотите.

Если все, что вам нужно, это целые числа, не используйте общий, что не является общим; или еще лучше, отклоните любой другой тип, проверив его тип.

Ответ 11

В чем смысл упражнения?

Как уже указывали люди, у вас может быть функция, не имеющая общих функций, занимающая самый большой элемент, а компилятор автоматически преобразует меньшие ints для вас.

static bool IntegerFunction(Int64 value) { }

Если ваша функция находится на критическом по производительности пути (очень маловероятно, IMO), вы можете обеспечить перегрузку для всех необходимых функций.

static bool IntegerFunction(Int64 value) { }
...
static bool IntegerFunction(Int16 value) { }

Ответ 12

Я бы использовал общий, который вы могли бы обрабатывать внешним...

/// <summary>
/// Generic object copy of the same type
/// </summary>
/// <typeparam name="T">The type of object to copy</typeparam>
/// <param name="ObjectSource">The source object to copy</param>
public T CopyObject<T>(T ObjectSource)
{
    T NewObject = System.Activator.CreateInstance<T>();

    foreach (PropertyInfo p in ObjectSource.GetType().GetProperties())
        NewObject.GetType().GetProperty(p.Name).SetValue(NewObject, p.GetValue(ObjectSource, null), null);

    return NewObject;
}

Ответ 13

Это ограничение повлияло на меня, когда я пытался перегрузить операторы для общих типов; так как не было ограничений "INumeric", и для множества других причин хорошие люди в stackoverflow с удовольствием предоставляют, операции не могут быть определены в родовых типах.

Мне хотелось что-то вроде

public struct Foo<T>
{
    public T Value{ get; private set; }

    public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
    {
        return new Foo<T> { Value = LHS.Value + RHS.Value; };
    }
}

Я работал над этой проблемой с использованием динамического ввода времени .net4.

public struct Foo<T>
{
    public T Value { get; private set; }

    public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
    {
        return new Foo<T> { Value = LHS.Value + (dynamic)RHS.Value };
    }
}

Две вещи об использовании dynamic:

  • Производительность. Все типы значений получаются в коробке.
  • Ошибки времени выполнения. Вы "били" компилятор, но теряете безопасность. Если общий тип не имеет определенного оператора, во время выполнения будет выбрано исключение.

Ответ 14

Пока нет "хорошего" решения. Однако вы можете сузить аргумент типа значительно, чтобы исключить многие ошибки для вашего гипотетического ограничения "INumeric", как показано выше.

static bool IntegerFunction <T> (значение T), где T: IComparable, IFormattable, IConvertible, IComparable <T> , IEquatable <T> , struct {...

Ответ 15

Типичные примитивные типы .NET не имеют общего интерфейса, который бы позволял использовать их для вычислений. Можно было бы определить ваши собственные интерфейсы (например, ISignedWholeNumber), которые будут выполнять такие операции, определять структуры, которые содержат один Int16, Int32 и т.д., И реализовывать эти интерфейсы, а затем использовать методы, которые принимают общие типы ограничено ISignedWholeNumber, но преобразование числовых значений в ваши типы структуры, вероятно, будет неприятным.

Альтернативным подходом было бы определить статический класс Int64Converter<T> со статическим свойством bool Available {get;}; и статическими делегатами для Int64 GetInt64(T value), T FromInt64(Int64 value), bool TryStoreInt64(Int64 value, ref T dest). Конструктор класса может использовать жесткую кодировку для загрузки делегатов для известных типов и, возможно, использовать Reflection для проверки того, реализует ли тип T методы с собственными именами и сигнатурами (в случае, если это что-то вроде структуры, содержащей Int64 и представляет число, но имеет собственный метод ToString()). Такой подход потеряет преимущества, связанные с проверкой типа времени компиляции, но все равно удастся избежать операций в боксе, и каждый тип нужно будет только "проверять" один раз. После этого операции, связанные с этим типом, будут заменены диспетчером делегатов.

Ответ 16

Если вы используете .NET 4.0 и позже, вы можете просто использовать динамический как аргумент метода и проверить во время выполнения, что прошедший динамический Тип аргумента - числовой/целочисленный тип.

Если тип прошедшего динамического не числового/целочисленного типа, то генерируйте исключение.

Пример короткий код, который реализует идею, выглядит примерно так:

using System;
public class InvalidArgumentException : Exception
{
    public InvalidArgumentException(string message) : base(message) {}
}
public class InvalidArgumentTypeException : InvalidArgumentException
{
    public InvalidArgumentTypeException(string message) : base(message) {}
}
public class ArgumentTypeNotIntegerException : InvalidArgumentTypeException
{
    public ArgumentTypeNotIntegerException(string message) : base(message) {}
}
public static class Program
{
    private static bool IntegerFunction(dynamic n)
    {
        if (n.GetType() != typeof(Int16) &&
            n.GetType() != typeof(Int32) &&
            n.GetType() != typeof(Int64) &&
            n.GetType() != typeof(UInt16) &&
            n.GetType() != typeof(UInt32) &&
            n.GetType() != typeof(UInt64))
            throw new ArgumentTypeNotIntegerException("argument type is not integer type");
        //code that implements IntegerFunction goes here
    }
    private static void Main()
    {
         Console.WriteLine("{0}",IntegerFunction(0)); //Compiles, no run time error and first line of output buffer is either "True" or "False" depends on the code that implements "Program.IntegerFunction" static method.
         Console.WriteLine("{0}",IntegerFunction("string")); //Also compiles but it is run time error and exception of type "ArgumentTypeNotIntegerException" is thrown here.
         Console.WriteLine("This is the last Console.WriteLine output"); //Never reached and executed due the run time error and the exception thrown on the second line of Program.Main static method.
    }

Конечно, это решение работает только во время выполнения, но не во время компиляции.

Если вы хотите, чтобы решение всегда работало во время компиляции и никогда не было во время выполнения, вам придется привязать динамический к публичной структуре/классу, чей перегруженный общедоступный конструкторы принимают только аргументы желаемых типов и присваивают соответствующее имя struct/class.

Имеет смысл, что обернутый динамический всегда частный член класса /struct и является единственным членом класса struct/class и именем единственного членом структуры/класса является "значение".

Вам также необходимо будет определить и реализовать методы public и/или операторы, которые будут работать с нужными типами для частного динамического члена класса/структуры, если это необходимо.

Также имеет смысл, что struct/class имеет конструктор special/unique, который принимает dynamic как аргумент, который инициализирует его только частным динамическим членом с именем "значение", но модификатор этого конструктора частный, конечно.

После того, как класс/структура будет готов, тип аргумента IntegerFunction будет определять тот класс/структуру, который был определен.

Пример длинный код, реализующий эту идею, выглядит примерно так:

using System;
public struct Integer
{
    private dynamic value;
    private Integer(dynamic n) { this.value = n; }
    public Integer(Int16 n) { this.value = n; }
    public Integer(Int32 n) { this.value = n; }
    public Integer(Int64 n) { this.value = n; }
    public Integer(UInt16 n) { this.value = n; }
    public Integer(UInt32 n) { this.value = n; }
    public Integer(UInt64 n) { this.value = n; }
    public Integer(Integer n) { this.value = n.value; }
    public static implicit operator Int16(Integer n) { return n.value; }
    public static implicit operator Int32(Integer n) { return n.value; }
    public static implicit operator Int64(Integer n) { return n.value; }
    public static implicit operator UInt16(Integer n) { return n.value; }
    public static implicit operator UInt32(Integer n) { return n.value; }
    public static implicit operator UInt64(Integer n) { return n.value; }
    public static Integer operator +(Integer x, Int16 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, Int32 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, Int64 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, UInt16 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, UInt32 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, UInt64 y) { return new Integer(x.value + y); }
    public static Integer operator -(Integer x, Int16 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, Int32 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, Int64 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, UInt16 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, UInt32 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, UInt64 y) { return new Integer(x.value - y); }
    public static Integer operator *(Integer x, Int16 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, Int32 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, Int64 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, UInt16 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, UInt32 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, UInt64 y) { return new Integer(x.value * y); }
    public static Integer operator /(Integer x, Int16 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, Int32 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, Int64 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, UInt16 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, UInt32 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, UInt64 y) { return new Integer(x.value / y); }
    public static Integer operator %(Integer x, Int16 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, Int32 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, Int64 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, UInt16 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, UInt32 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, UInt64 y) { return new Integer(x.value % y); }
    public static Integer operator +(Integer x, Integer y) { return new Integer(x.value + y.value); }
    public static Integer operator -(Integer x, Integer y) { return new Integer(x.value - y.value); }
    public static Integer operator *(Integer x, Integer y) { return new Integer(x.value * y.value); }
    public static Integer operator /(Integer x, Integer y) { return new Integer(x.value / y.value); }
    public static Integer operator %(Integer x, Integer y) { return new Integer(x.value % y.value); }
    public static bool operator ==(Integer x, Int16 y) { return x.value == y; }
    public static bool operator !=(Integer x, Int16 y) { return x.value != y; }
    public static bool operator ==(Integer x, Int32 y) { return x.value == y; }
    public static bool operator !=(Integer x, Int32 y) { return x.value != y; }
    public static bool operator ==(Integer x, Int64 y) { return x.value == y; }
    public static bool operator !=(Integer x, Int64 y) { return x.value != y; }
    public static bool operator ==(Integer x, UInt16 y) { return x.value == y; }
    public static bool operator !=(Integer x, UInt16 y) { return x.value != y; }
    public static bool operator ==(Integer x, UInt32 y) { return x.value == y; }
    public static bool operator !=(Integer x, UInt32 y) { return x.value != y; }
    public static bool operator ==(Integer x, UInt64 y) { return x.value == y; }
    public static bool operator !=(Integer x, UInt64 y) { return x.value != y; }
    public static bool operator ==(Integer x, Integer y) { return x.value == y.value; }
    public static bool operator !=(Integer x, Integer y) { return x.value != y.value; }
    public override bool Equals(object obj) { return this == (Integer)obj; }
    public override int GetHashCode() { return this.value.GetHashCode(); }
    public override string ToString() { return this.value.ToString(); }
    public static bool operator >(Integer x, Int16 y) { return x.value > y; }
    public static bool operator <(Integer x, Int16 y) { return x.value < y; }
    public static bool operator >(Integer x, Int32 y) { return x.value > y; }
    public static bool operator <(Integer x, Int32 y) { return x.value < y; }
    public static bool operator >(Integer x, Int64 y) { return x.value > y; }
    public static bool operator <(Integer x, Int64 y) { return x.value < y; }
    public static bool operator >(Integer x, UInt16 y) { return x.value > y; }
    public static bool operator <(Integer x, UInt16 y) { return x.value < y; }
    public static bool operator >(Integer x, UInt32 y) { return x.value > y; }
    public static bool operator <(Integer x, UInt32 y) { return x.value < y; }
    public static bool operator >(Integer x, UInt64 y) { return x.value > y; }
    public static bool operator <(Integer x, UInt64 y) { return x.value < y; }
    public static bool operator >(Integer x, Integer y) { return x.value > y.value; }
    public static bool operator <(Integer x, Integer y) { return x.value < y.value; }
    public static bool operator >=(Integer x, Int16 y) { return x.value >= y; }
    public static bool operator <=(Integer x, Int16 y) { return x.value <= y; }
    public static bool operator >=(Integer x, Int32 y) { return x.value >= y; }
    public static bool operator <=(Integer x, Int32 y) { return x.value <= y; }
    public static bool operator >=(Integer x, Int64 y) { return x.value >= y; }
    public static bool operator <=(Integer x, Int64 y) { return x.value <= y; }
    public static bool operator >=(Integer x, UInt16 y) { return x.value >= y; }
    public static bool operator <=(Integer x, UInt16 y) { return x.value <= y; }
    public static bool operator >=(Integer x, UInt32 y) { return x.value >= y; }
    public static bool operator <=(Integer x, UInt32 y) { return x.value <= y; }
    public static bool operator >=(Integer x, UInt64 y) { return x.value >= y; }
    public static bool operator <=(Integer x, UInt64 y) { return x.value <= y; }
    public static bool operator >=(Integer x, Integer y) { return x.value >= y.value; }
    public static bool operator <=(Integer x, Integer y) { return x.value <= y.value; }
    public static Integer operator +(Int16 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(Int32 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(Int64 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(UInt16 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(UInt32 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(UInt64 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator -(Int16 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(Int32 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(Int64 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(UInt16 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(UInt32 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(UInt64 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator *(Int16 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(Int32 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(Int64 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(UInt16 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(UInt32 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(UInt64 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator /(Int16 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(Int32 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(Int64 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(UInt16 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(UInt32 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(UInt64 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator %(Int16 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(Int32 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(Int64 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(UInt16 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(UInt32 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(UInt64 x, Integer y) { return new Integer(x % y.value); }
    public static bool operator ==(Int16 x, Integer y) { return x == y.value; }
    public static bool operator !=(Int16 x, Integer y) { return x != y.value; }
    public static bool operator ==(Int32 x, Integer y) { return x == y.value; }
    public static bool operator !=(Int32 x, Integer y) { return x != y.value; }
    public static bool operator ==(Int64 x, Integer y) { return x == y.value; }
    public static bool operator !=(Int64 x, Integer y) { return x != y.value; }
    public static bool operator ==(UInt16 x, Integer y) { return x == y.value; }
    public static bool operator !=(UInt16 x, Integer y) { return x != y.value; }
    public static bool operator ==(UInt32 x, Integer y) { return x == y.value; }
    public static bool operator !=(UInt32 x, Integer y) { return x != y.value; }
    public static bool operator ==(UInt64 x, Integer y) { return x == y.value; }
    public static bool operator !=(UInt64 x, Integer y) { return x != y.value; }
    public static bool operator >(Int16 x, Integer y) { return x > y.value; }
    public static bool operator <(Int16 x, Integer y) { return x < y.value; }
    public static bool operator >(Int32 x, Integer y) { return x > y.value; }
    public static bool operator <(Int32 x, Integer y) { return x < y.value; }
    public static bool operator >(Int64 x, Integer y) { return x > y.value; }
    public static bool operator <(Int64 x, Integer y) { return x < y.value; }
    public static bool operator >(UInt16 x, Integer y) { return x > y.value; }
    public static bool operator <(UInt16 x, Integer y) { return x < y.value; }
    public static bool operator >(UInt32 x, Integer y) { return x > y.value; }
    public static bool operator <(UInt32 x, Integer y) { return x < y.value; }
    public static bool operator >(UInt64 x, Integer y) { return x > y.value; }
    public static bool operator <(UInt64 x, Integer y) { return x < y.value; }
    public static bool operator >=(Int16 x, Integer y) { return x >= y.value; }
    public static bool operator <=(Int16 x, Integer y) { return x <= y.value; }
    public static bool operator >=(Int32 x, Integer y) { return x >= y.value; }
    public static bool operator <=(Int32 x, Integer y) { return x <= y.value; }
    public static bool operator >=(Int64 x, Integer y) { return x >= y.value; }
    public static bool operator <=(Int64 x, Integer y) { return x <= y.value; }
    public static bool operator >=(UInt16 x, Integer y) { return x >= y.value; }
    public static bool operator <=(UInt16 x, Integer y) { return x <= y.value; }
    public static bool operator >=(UInt32 x, Integer y) { return x >= y.value; }
    public static bool operator <=(UInt32 x, Integer y) { return x <= y.value; }
    public static bool operator >=(UInt64 x, Integer y) { return x >= y.value; }
    public static bool operator <=(UInt64 x, Integer y) { return x <= y.value; }
}
public static class Program
{
    private static bool IntegerFunction(Integer n)
    {
        //code that implements IntegerFunction goes here
        //note that there is NO code that checks the type of n in rum time, because it is NOT needed anymore 
    }
    private static void Main()
    {
        Console.WriteLine("{0}",IntegerFunction(0)); //compile error: there is no overloaded METHOD for objects of type "int" and no implicit conversion from any object, including "int", to "Integer" is known.
        Console.WriteLine("{0}",IntegerFunction(new Integer(0))); //both compiles and no run time error
        Console.WriteLine("{0}",IntegerFunction("string")); //compile error: there is no overloaded METHOD for objects of type "string" and no implicit conversion from any object, including "string", to "Integer" is known.
        Console.WriteLine("{0}",IntegerFunction(new Integer("string"))); //compile error: there is no overloaded CONSTRUCTOR for objects of type "string"
    }
}

Обратите внимание, что для использования динамического в вашем коде вы должны Добавить ссылку в Microsoft.CSharp

Если версия .NET Framework ниже/меньше/меньше 4,0, а динамическая - undefined в этой версии, тогда вам придется использовать object и делайте кастинг на целочисленный тип, что является проблемой, поэтому я рекомендую использовать хотя бы .NET 4.0 или новее, если вы можете использовать динамический вместо объекта.

Ответ 17

Если все, что вы хотите, использует один числовой тип, вы можете подумать о создании чего-то похожего на псевдоним в C++ с using.

Поэтому вместо того, чтобы иметь очень общий

T ComputeSomething<T>(T value1, T value2) where T : INumeric { ... }

вы могли бы

using MyNumType = System.Double;
T ComputeSomething<MyNumType>(MyNumType value1, MyNumType value2) { ... }

Это может позволить вам легко перейти от double к int или другим, если это необходимо, но вы не сможете использовать ComputeSomething с double и int в той же программе.

Но почему бы не заменить все double на int тогда? Потому что ваш метод может захотеть использовать double, является ли вход double или int. Псевдоним позволяет вам точно знать, какая переменная использует динамический тип.

Ответ 18

У меня была похожая ситуация, когда мне нужно было обрабатывать числовые типы и строки; кажется немного странным миксом, но все готово.

Опять же, как и многие люди, я посмотрел на ограничения и придумал несколько интерфейсов, которые он должен был поддерживать. Тем не менее, а) это не было на 100% водонепроницаемо и б), любой новичок, взглянувший на этот длинный список ограничений, сразу же был бы очень смущен.

Итак, мой подход состоял в том, чтобы поместить всю мою логику в универсальный метод без ограничений, но сделать этот универсальный метод закрытым. Затем я раскрыл его с помощью открытых методов, один из которых явно обрабатывал тип, который я хотел обработать - на мой взгляд, код является чистым и явным, например,

public static string DoSomething(this int input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this decimal input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this double input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this string input, ...) => DoSomethingHelper(input, ...);

private static string DoSomethingHelper<T>(this T input, ....)
{
    // complex logic
}

Ответ 19

Нет единого интерфейса или базового класса, который все они наследуют (который также не унаследован другими классами), поэтому простой ответ - нет.

Я действительно удивляюсь, почему это проблема. Что вы хотите сделать в своем классе IntegerFunction, который может быть сделан только для целых чисел?

Ответ 20

Я думаю, вы недопонимаете дженерики. Если операция, которую вы пытаетесь выполнить, хороша только для определенных типов данных, тогда вы не делаете что-то "общее".

Кроме того, поскольку вы хотите, чтобы функция работала только с типами данных int, вам не потребуется отдельная функция для каждого конкретного размера. Простое использование параметра в самом большом конкретном типе позволит программе автоматически подгонять к нему более мелкие типы данных. (то есть передача Int16 будет автоматически конвертировать в Int64 при вызове).

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

В противном случае можно использовать параметр типа Object, а затем вам нужно будет проверить тип параметра и предпринять соответствующие действия или выбросить исключение.