2010-04-21 12 views
62

me gustaría fijar un valor x a una gama [a, b]:¿Dónde puedo encontrar la función "pinza" en .NET?

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

Esto es bastante básico. Pero no veo una función "abrazadera" en la biblioteca de clases, al menos no en System.Math.

(Para que los inadvertidos "clamp" un valor es asegurarse de que se encuentra entre algunos valores máximo y mínimo. Si es mayor que el valor máximo, entonces es reemplazado por el máximo, etc.)

+1

@Danvil: No hay "Biblioteca de clases C#". Te refieres a "The .NET Framework". –

+0

¿Todavía no hay nada desde C# 7.1? – joce

+0

@ JohnSaunders No creo que sea estrictamente cierto https://stackoverflow.com/questions/807880/bcl-base-class-library-vs-fcl-framework-class-library –

Respuesta

88

Se puede escribir un método de extensión:

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: Los métodos de extensión van en clases estáticas, ya que esta es una función bastante de bajo nivel, probablemente debería ir en algún núcleo espacio de nombres en tu proyecto A continuación, puede usar el método en cualquier archivo de código que contenga una directiva using para el espacio de nombres, p.

using Core.ExtensionMethods 

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

¿Dónde pondré esto y estoy llamando CompareTo más lento que comparando con <(para tipos integrales)? – Danvil

+1

En una clase estática y en el marco .NET (no estoy seguro acerca de mono, compacto, etc.), el genérico debe recompilarse para el tipo, y CompareTo debe estar en línea, por lo que no hay penalización de rendimiento. –

+1

@Frasier A menos que sea un código ultra sensible al rendimiento, es poco probable que obtenga un rendimiento significativo al hacerlo. Tenerlo ser genérico es probablemente más útil que guardar unos pocos microsegundos. – MgSam

10

No hay una, pero no es demasiado difícil hacer una. He encontrado uno aquí: clamp

Es:

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

Y puede ser utilizado como:

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

Esta solución es mejor que la aceptada. Sin ambigüedad – aggsol

+4

@CodeClown Esta solución da como resultado una comparación innecesaria cuando value> max, y el orden inverso de argumentos invita (y prácticamente garantiza) errores. No sé qué ambigüedad crees que se evita. –

19

Probar:

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

ilegible, pero bueno un trazador de líneas! – Larry

+3

@Larry, ¿Cómo es eso ilegible? Bien para mí al menos; +1 – kim366

+2

¡Uf! Esos feos paréntesis redundantes! ¡Si vas a ser un genio malvado con los operadores ternarios dobles, al menos hazlo bien y deshazte de ellos también! – XenoRo

13

Sólo tiene que utilizar Math.Min y Math.Max:

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

Eso se traduce en 'int a0 = x> a? x: a; devuelve a0

+10

y por qué es eso? – d7samurai

+2

@ d7samurai Si sabemos que min <= max, 'Math.Min (Math.Max ​​(x, min), max)' da como resultado una comparación más de la necesaria si x

0

Sólo compartir Lee's solution con temas los comentarios y preocupaciones sean tratadas, siempre que sea posible:

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

Diferencias:

Limitaciones: No one-sided clamps. Si max es NaN, siempre devuelve NaN (Consulte Herman's comment).

0

Usando las respuestas anteriores, lo resumí en el siguiente código para mis necesidades. Esto también le permitirá fijar un número solo por su mínimo o máximo.

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