2010-04-21 2 views
62

Я хотел бы, чтобы зажать значение x в диапазоне [a, b]:Где я могу найти функцию «зажима» в .NET?

x = (x < a) ? a : ((x > b) ? b : x); 

Это довольно простой. Но я не вижу функции «зажима» в библиотеке классов - по крайней мере, не в System.Math.

(Для не знают, чтобы «зажать» значение, чтобы убедиться, что она лежит между некоторым максимальным и минимальным значениями. Если это больше, чем максимальное значение, то оно заменяется на максимум, и т.д.)

+1

@ Danvil: Нет библиотеки классов C#. Вы имеете в виду «.NET Framework». –

+0

Все еще ничего с C# 7.1? – joce

+0

@JohnSaunders Я не верю, что это строго верно https://stackoverflow.com/questions/807880/bcl-base-class-library-vs-fcl-framework-class-library –

ответ

88

Вы могли бы написать метод расширения:

public static T Clamp<T>(this T val, T min, T max) where T : IComparable<T> 
{ 
    if (val.CompareTo(min) < 0) return min; 
    else if(val.CompareTo(max) > 0) return max; 
    else return val; 
} 

EDIT: Методы расширения выполняются в статических классах - поскольку это довольно низкоуровневая функция, она, вероятно, должна идти в некотором ядре пространство имен в вашем проекте. Затем вы можете использовать этот метод в любом файле кода, который содержит директиву using для пространства имен, например.

using Core.ExtensionMethods 

int i = 4.Clamp(1, 3); 
+1

Где бы я положил это и вызывая CompareTo медленнее, чем сравнение с <(для целых типов)? – Danvil

+1

В статическом классе и в .NET Framework (не уверенном в моно, компактном и т. Д.), Общий тип должен быть перекомпилирован для типа, а CompareTo inlined, поэтому никакого снижения производительности. –

+1

@Frasier Если это не очень чувствительный к ультрапроизводительности код, вы вряд ли будете делать какие-либо значимые улучшения в производительности, сделав это. Наличие его общего, вероятно, более полезно, чем сохранение нескольких микросекунд. – MgSam

9

Существует не один в пространстве имен System.Math

http://msdn.microsoft.com/en-us/library/system.math_members.aspx

Существует MathHelper класса, где он доступен для XNA игровой студии, если это случается, то, что вы делаете:

http://msdn.microsoft.com/en-us/library/bb197892(v=XNAGameStudio.31).aspx

+1

+1 для MathHelper, хотя я не знаю, Думаю, я буду использовать его. –

10

Существует не один, но это не так сложно сделать. Я нашел здесь: clamp

Это:

public static T Clamp<T>(T value, T max, T min) 
    where T : System.IComparable<T> { 
     T result = value; 
     if (value.CompareTo(max) > 0) 
      result = max; 
     if (value.CompareTo(min) < 0) 
      result = min; 
     return result; 
    } 

И он может быть использован как:

int i = Clamp(12, 10, 0); -> i == 10 
double d = Clamp(4.5, 10.0, 0.0); -> d == 4.5 
+0

Это решение лучше, чем принятое. Нет двусмысленности. – aggsol

+4

@CodeClown Это решение приводит к ненужному сопоставлению, когда значение> max, а инвертированный порядок аргументов приглашает (и практически гарантирует) ошибки. Я не знаю, какую двусмысленность, по вашему мнению, можно избежать. –

19

Try:

public static int Clamp(int value, int min, int max) 
{ 
    return (value < min) ? min : (value > max) ? max : value; 
} 
+2

Непроницаемый, но хороший один вкладыш! – Larry

+3

@Larry, Как это нечитаемо? Отлично для меня; +1 – kim366

+2

Ух! Эти уродливые лишние круглые скобки! Если вы будете злым гением с двойными тройными операторами, по крайней мере, сделайте это правильно и избавитесь от них! – XenoRo

13

Просто используйте Math.Min и Math.Max:

x = Math.Min(Math.Max(x, a), b); 
+0

Что означает 'int a0 = x> a? x: a; return a0

+10

и почему? – d7samurai

+2

@ d7samurai Если мы знаем, что min <= max, 'Math.Min (Math.Max ​​(x, min), max)' приводит к еще одному сравнению, если необходимо, если x

0

Просто обмен Lee's solution с вопросами для комментариев и проблемы решены, где это возможно:

public static T Clamped<T>(this T value, T min, T max) where T : IComparable<T> { 
    if (value == null) throw new ArgumentNullException(nameof(value), "is null."); 
    if (min == null) throw new ArgumentNullException(nameof(min), "is null."); 
    if (max == null) throw new ArgumentNullException(nameof(max), "is null."); 
    //If min <= max, clamp 
    if (min.CompareTo(max) <= 0) return value.CompareTo(min) < 0 ? min : value.CompareTo(max) > 0 ? max : value; 
    //If min > max, clamp on swapped min and max 
    return value.CompareTo(max) < 0 ? max : value.CompareTo(min) > 0 ? min : value; 
} 

Отличия:

  • Название Метод использует соответствующий глагол времени (ed), чтобы (далее) показывают, что значение не зажата на месте, и что, вместо этого, новое значение возвращается (См @JimBalter's comment).
  • Соответствует null check на всех входах (см. @JeppeStigNielsen's comment).
  • свопы min и max, если min > max (См. @JeppeStigNielsen's comment).

Ограничения: Без односторонних зажимов. Если max - NaN, всегда возвращается NaN (см. Herman's comment).

0

Используя предыдущие ответы, я сконденсировал его до нижнего кода для моих нужд. Это также позволит вам зажимать номер только по его мин. Или макс.

public static class IComparableExtensions 
{ 
    public static T Clamped<T>(this T value, T min, T max) 
     where T : IComparable<T> 
    { 
     return value.CompareTo(min) < 0 ? min : value.ClampedMaximum(max); 
    } 

    public static T ClampedMinimum<T>(this T value, T min) 
     where T : IComparable<T> 
    { 
     return value.CompareTo(min) < 0 ? min : value; 
    } 

    public static T ClampedMaximum<T>(this T value, T max) 
     where T : IComparable<T> 
    { 
     return value.CompareTo(max) > 0 ? max : value; 
    } 
} 
Смежные вопросы