106 votes

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

Je voudrais fixer une valeur x à une gamme [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 - en tout cas pas dans System.Math .

(Pour l'inconscient, "brider" 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.)

2 votes

@Danvil : Il n'y a pas de "bibliothèque de classes C#". Vous voulez dire "The .NET Framework".

1 votes

Toujours rien à partir de C# 7.1 ?

2 votes

@JohnSaunders Je ne crois pas que ce soit strictement vrai. stackoverflow.com/questions/807880/

151voto

Lee Points 63849

Vous pourriez é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;
}

Les méthodes d'extension sont placées dans des classes statiques - comme il s'agit d'une fonction de bas niveau, elle devrait probablement être placée dans un espace de noms central de votre projet. Vous pouvez ensuite utiliser la méthode dans n'importe quel fichier de code qui contient une directive using pour l'espace de nom, par ex.

using Core.ExtensionMethods

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

.NET Core 2.0

Démarrer avec .NET Core 2.0 System.Math a maintenant un Clamp qui peut être utilisée à la place :

using System;

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

1 votes

Où dois-je mettre cela et est-ce que l'appel à CompareTo est plus lent que la comparaison avec < (pour les types entiers) ?

1 votes

Dans une classe statique, et dans le cadre .NET (je ne suis pas sûr pour mono, compact, etc.), le générique devrait être recompilé pour le type, et CompareTo inlined, donc pas de pénalité de performance.

1 votes

@Frasier A moins qu'il ne s'agisse d'un code très sensible aux performances, il est peu probable que vous fassiez des gains de performance significatifs en procédant ainsi. Le fait qu'il soit générique est probablement plus utile que de gagner quelques microsecondes.

36voto

d7samurai Points 732

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

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

0 votes

Cela se traduit par int a0 = x > a ? x : a; return a0 < b ? a0 : b ce qui (bien que donnant des résultats corrects) n'est pas vraiment idéal.

4 votes

@d7samurai Si on sait que min <= max, Math.Min(Math.Max(x, min), max) entraîne une comparaison de plus que nécessaire si x < min.

0 votes

@JimBalter, en théorie c'est vrai. Si vous regardez comment CompareTo() est typiquement implémenté, la réponse acceptée peut prendre jusqu'à 6 comparaisons. Je ne sais pas si le compilateur est suffisamment intelligent pour intégrer le CompareTo() et supprimer les comparaisons superflues.

26voto

Clit Points 81

Essayez :

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

7 votes

Ugh ! Ces affreuses parenthèses redondantes ! Si vous voulez être un génie maléfique avec les opérateurs ternaires doubles, faites-le au moins correctement et débarrassez-vous de ceux-ci aussi !

16 votes

@XenoRo Ces parenthèses "redondantes" sont ce qui le rend lisible.

2 votes

@Cleaner - 1) Si l'on recherche la lisibilité, il faut éviter les doubles ternaires et utiliser plutôt des blocs IF. 2) Vous ne comprenez pas la plaisanterie, n'est-ce pas ? xD

13voto

Jeremy B. Points 6079

Il n'y en a pas, mais ce n'est pas très difficile d'en faire un. J'en ai trouvé un ici : pince

Elle l'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

0 votes

Cette solution est meilleure que celle qui a été acceptée. Aucune ambiguïté.

6 votes

@CodeClown Cette solution entraîne une comparaison inutile lorsque la valeur > max, et l'ordre inversé des arguments invite (et garantit pratiquement) les bogues. Je ne sais pas quelle ambiguïté vous pensez éviter.

0 votes

Pour des raisons de cohérence avec l'ancienne implémentation de Math.Clamp, il est recommandé de changer l'ordre des paramètres min/max : Clamp(T value, T min, T max)

10voto

Kevin Points 57797

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

Il existe un MathHelper où il est disponible pour le Studio de jeux XNA si c'est ce que vous faites :

0 votes

Maintenant, c'est le cas : Math.Clamp

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X