web-dev-qa-db-fra.com

Où puis-je trouver la fonction "clamp" dans .NET?

Je voudrais attacher une valeur x à une plage [a, b]:

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

C'est assez basique. Mais je ne vois pas de fonction "clamp" dans la bibliothèque de classes - du moins pas dans System.Math.

(Pour l’ignorant, "verrouiller" une valeur consiste à s’assurer qu’elle se situe entre certaines valeurs maximales et minimales. Si elle est supérieure à la valeur maximale, elle est remplacée par la valeur maximale, etc.)

80
Danvil

Vous pouvez écrire une méthode d'extension:

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: les méthodes d’extension vont dans des classes statiques - comme il s’agit d’une fonction de bas niveau, elle devrait probablement aller dans un espace de noms principal de votre projet. Vous pouvez ensuite utiliser la méthode dans n’importe quel fichier de code contenant une directive using pour l’espace de nommage, par exemple.

using Core.ExtensionMethods

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

.NET Core 2.0

À partir de .NET Core 2.0, System.Math a maintenant une méthode Clamp qui peut être utilisée à la place:

using System;

int i = Math.Clamp(4, 1, 3);
109
Lee

Essayer:

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

Il suffit d'utiliser Math.Min et Math.Max:

x = Math.Min(Math.Max(x, a), b);
21
d7samurai

Il n'y en a pas, mais ce n'est pas trop difficile d'en faire un. J'en ai trouvé un ici: clamp

Il est:

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

Et il peut être utilisé comme:

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

Il n'y en a pas dans l'espace de noms System.Math

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

Il existe une classe MathHelper où elle est disponible pour le studio de jeu XNA si c'est ce que vous faites:

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

10
kemiller2002

Just sharing La solution de Lee avec les problèmes, commentaires et préoccupations traités, dans la mesure du possible:

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

Différences:

Limitations: Pas de pinces unilatérales. Si max est NaN, retourne toujours NaN (voir commentaire de Herman ).

3
XenoRo

Le code ci-dessous prend en charge la spécification de bornes dans n’importe quel ordre (par exemple bound1 <= bound2 ou bound2 <= bound1). J'ai trouvé cela utile pour le serrage de valeurs calculées à partir d'équations linéaires (y=mx+b) où la pente de la ligne peut augmenter ou diminuer.

Je sais: le code se compose de cinq opérateurs d’expression conditionnelle super-laids . La chose est, cela fonctionne , et les tests ci-dessous le prouvent. N'hésitez pas à ajouter des parenthèses strictement inutiles si vous le souhaitez.

Vous pouvez facilement créer d'autres surcharges pour d'autres types numériques et simplement copier/coller les tests.

Avertissement: Comparer les nombres en virgule flottante n'est pas simple. Ce code n'implémente pas les comparaisons double avec robustesse. Utilisez une bibliothèque de comparaison en virgule flottante pour remplacer les utilisations des opérateurs de comparaison.

public static class MathExtensions
{
    public static double Clamp(this double value, double bound1, double bound2)
    {
        return bound1 <= bound2 ? value <= bound1 ? bound1 : value >= bound2 ? bound2 : value : value <= bound2 ? bound2 : value >= bound1 ? bound1 : value;
    }
}

tests xUnit/FluentAssertions:

public class MathExtensionsTests
{
    [Theory]
    [InlineData(0, 0, 0, 0)]
    [InlineData(0, 0, 2, 0)]
    [InlineData(-1, 0, 2, 0)]
    [InlineData(1, 0, 2, 1)]
    [InlineData(2, 0, 2, 2)]
    [InlineData(3, 0, 2, 2)]
    [InlineData(0, 2, 0, 0)]
    [InlineData(-1, 2, 0, 0)]
    [InlineData(1, 2, 0, 1)]
    [InlineData(2, 2, 0, 2)]
    [InlineData(3, 2, 0, 2)]
    public void MustClamp(double value, double bound1, double bound2, double expectedValue)
    {
        value.Clamp(bound1, bound2).Should().Be(expectedValue);
    }
}
0
NathanAldenSr

En utilisant les réponses précédentes, je l'ai résumé au code ci-dessous pour mes besoins. Cela vous permettra également de fixer un nombre uniquement par son minimum ou maximum.

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;
    }
}
0
Bobby Speirs