C #: общие математические функции (Мин, Макс и т. Д.) - PullRequest
13 голосов
/ 15 декабря 2009

Я думал о написании универсальных функций для базовых математических операций, таких как Min, Max и т. Д. Но я не знаю, как сравнить два универсальных типа:

public T Max<T>(T v1, T v2) where T: struct
{
   return (v1 > v2 ? v1 : v2);
}

Как насчет этого?

Спасибо.

Ответы [ 5 ]

22 голосов
/ 15 декабря 2009

Если вы хотите создать только функции сравнения, вы можете использовать стандартный компаратор для типа T. Например:

public static T Max<T>(T x, T y)
{
    return (Comparer<T>.Default.Compare(x, y) > 0) ? x : y;
}

Если T реализует IComparable<T>, то этот компаратор будет использоваться; если T не реализует IComparable<T>, но реализует IComparable, то будет использоваться этот компаратор; если T не реализует IComparable<T> или IComparable, то будет выдано исключение времени выполнения.

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

22 голосов
/ 15 декабря 2009

Вы, вероятно, хотите ограничить универсальные типы для реализации IComparable:

public T Max<T>(T v1, T v2) where T: struct, IComparable<T>

, а затем используйте метод CompareTo:

{
    return (v1.CompareTo(v2) > 0 ? v1 : v2);
}
4 голосов
/ 11 ноября 2013

Позвольте мне не согласиться. Реализация @ LukeH не является общей .

Я объясню, почему это не Generic:

Comparer<T>.Default включает проверку T во время выполнения, чтобы определить, реализует ли он IComparable<T>, IComparable или нет. Хотя это поведение недостаточно документировано в http://msdn.microsoft.com/en-us/library/azhsac5f.aspx,, мы можем вычесть его, потому что Comparer<T>.Default выдает исключение, когда T не реализует ни того, ни другого. Если бы проверка была выполнена во время компиляции, исключение (время выполнения) не потребовалось бы, с ошибкой времени компиляции.

Тогда, так как Comparer<T>.Default использует Reflection, это означает высокую стоимость времени выполнения, тогда ...., Это НЕ универсальный ... Почему?

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

Возьмите пример. Рукописная версия для целых чисел будет:

public static int Max( int x, int y)
{
    return (x.CompareTo(y) > 0) ? x : y;
}

Это очень просто, включая только сравнение (или, может быть, больше, в зависимости от того, как реализовано Int32.CompareTo ()). Если мы используем реализацию @ LukeH, мы добавляем Reflection к чему-то очень простому.

Короче говоря:

  1. Всегда предпочитайте ошибки времени компиляции исключениям времени выполнения (это не Javascript, Ruby, ... :-))
  2. Эта реализация менее эффективна по сравнению с рукописной версией (для любого типа)

С другой стороны. Что, по вашему мнению, должен вернуть Макс, когда x и y являются эквивалентами?

Я начинаю анализировать реализации Real-Generic ....

Идеальной реализацией было бы что-то вроде ...

    public static T Max<T>(T x, T y, Func<T, T, int> cmp)
    {
        return (cmp(x, y) > 0) ? x : y;
    }

    //Pseudo-code ( note the 'or' next to 'where' )
    public static T Max<T>(T x, T y) where T: IComparable<T> or IComparable
    {
        return Max(x, y, (a, b) => { return a.CompareTo(b); });
    }

Это невозможно в C #, следующая попытка может быть ...

    //pseudo-code
    public static T Max<T>(T x, T y, Func<T, T, int> cmp)
    {
        return (cmp(x, y) > 0) ? x : y;
    }

    public static T Max<T>(T x, T y) where T: IComparable<T>
    {
        return Max(x, y, (a, b) => { return a.CompareTo(b); });
    }

    public static T Max<T>(T x, T y) where T: IComparable
    {
        return Max(x, y, (a, b) => { return a.CompareTo(b); });
    }

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

Тогда я сознательно пропущу IComparable. Я просто буду беспокоиться о IComparable<T>

    public static T Max<T>(T x, T y, Func<T, T, int> cmp)
    {
        return (cmp(x, y) > 0) ? x : y;
    }

    public static T Max<T>(T x, T y) where T: IComparable<T>
    {
        return Max(x, y, (a, b) => { return a.CompareTo(b); });
    }
3 голосов
/ 05 мая 2012

Это немного поздно, но почему бы не использовать динамические типы и делегаты в качестве альтернативы IComparable? Таким образом, вы получаете безопасность типа компиляции в большинстве случаев и получите ошибку времени выполнения только в том случае, если оба типа не поддерживают оператор <и вы не можете предоставить компаратор по умолчанию в качестве аргумента. </p>

public static T Max<T>(T first, T second, Func<T,T,bool> f = null)
{
    Func<dynamic,dynamic,bool> is_left_smaller = (x, y) => x < y ? true : false;

    var compare = f ?? new Func<T, T, bool>((x, y) => is_left_smaller(x, y));

    return compare(first, second) ? second : first; 
}
0 голосов
/ 15 декабря 2009

Из памяти T также должно быть IComparable (добавьте это к where), а затем вы используете v1.CompareTo(v2) > 0 и т. Д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...