Où puis-je trouver la fonction « pince » dans .NET?
Question
Je voudrais serrer une x
de valeur à une plage [a, b]
:
x = (x < a) ? a : ((x > b) ? b : x);
Ceci est assez basique. Mais je ne vois pas une fonction « pince » dans la bibliothèque de classes -. Au moins pas dans System.Math
(Pour le pas au courant de « pince » une valeur est de vous assurer qu'il se trouve entre certaines valeurs maximales et minimales. Si elle est supérieure à la valeur maximale, il est remplacé par le maximum, etc.)
La solution
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: Méthodes d'extension vont dans les classes statiques - car cela est tout à fait fonction de bas niveau, il devrait aller probablement dans un espace de noms de base dans votre projet. Vous pouvez ensuite utiliser la méthode dans un fichier de code qui contient une directive à l'aide de l'espace de noms par exemple.
using Core.ExtensionMethods
int i = 4.Clamp(1, 3);
.NET Core 2.0
A partir de System.Math
.NET Core 2.0 a maintenant un Clamp
méthode qui peut être utilisé à la place:
using System;
int i = Math.Clamp(4, 1, 3);
Autres conseils
Essayez:
public static int Clamp(int value, int min, int max)
{
return (value < min) ? min : (value > max) ? max : value;
}
Il suffit d'utiliser Math.Min
et Math.Max
:
x = Math.Min(Math.Max(x, a), b);
Il n'y a pas un, mais il est pas trop difficile de faire un. J'ai trouvé un ici:
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
Il n'y a pas un dans l'espace de noms System.Math
http://msdn.microsoft.com/en-us/ bibliothèque / system.math_members.aspx
Il y a une classe MathHelper où il est disponible pour le studio de jeu XNA si cela arrive à être ce que vous faites:
http://msdn.microsoft.com /en-us/library/bb197892(v=XNAGameStudio.31).aspx
solution de Lee avec les questions et les préoccupations des commentaires adressés, si 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:
- Nom de la méthode utilise le verbe approprié (
ed
) à (plus) indiquent que la valeur est fixée en place, et que, au contraire, une nouvelle valeur est retournée (Voir @ commentaire de JimBalter). - Est-
null check
appropriée sur toutes les entrées (voir @ commentaire de JeppeStigNielsen). - Swaps
min
etmax
simin > max
(Voir @ commentaire de JeppeStigNielsen).
Limitations:
Pas de pinces à sens unique. Si max
est NaN
, retourne toujours NaN
(Voir commentaire de Herman de).
En utilisant les réponses précédentes, je condensé vers le bas au-dessous du code pour mes besoins. Cela vous permettra également de serrer un seul numéro par son min ou max.
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;
}
}
Le dessous des supports de code spécifiant des limites dans un ordre quelconque (par exemple bound1 <= bound2
ou bound2 <= bound1
). J'ai trouvée utile pour des valeurs de serrage calculées à partir des équations linéaires (y=mx+b
) où la pente de la ligne peut être croissante ou décroissante.
Je sais: Le code se compose de cinq opérateurs d'expression conditionnelle super-vilains . La chose est, cela fonctionne , et les tests prouvent en dessous. Ne hésitez pas à ajouter entre parenthèses strictement inutiles si vous le désirez.
Vous pouvez facilement créer d'autres pour d'autres types de surcharges numériques et essentiellement copier / coller les tests.
Attention: La comparaison des nombres à virgule flottante n'est pas simple. Ce code ne met pas en œuvre des comparaisons double
avec vigueur. Utilisez une bibliothèque de comparaison à 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;
}
}
xUnit / tests 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);
}
}