2010-07-04 6 views
21

En C#, je dois souvent limiter une valeur entière à une plage de valeurs. Par exemple, si une application attend un pourcentage, un entier d'une entrée utilisateur ne doit pas être inférieur à zéro ou supérieur à cent. Un autre exemple: s'il y a cinq pages Web qui sont accessibles par Request.Params["p"], j'attends une valeur de 1 à 5, de 0 ou 256 ou 99999.Comment forcer un nombre à être dans une plage en C#?

Je termine souvent en écrivant un code assez laid comme:

page = Math.Max(0, Math.Min(2, page)); 

ou encore plus laid:

percentage = 
    (inputPercentage < 0 || inputPercentage > 100) ? 
    0 : 
    inputPercentage; 

est pas là une façon plus intelligente de faire de telles choses dans .NET Framework?

Je sais que je peux écrire une méthode générale int LimitToRange(int value, int inclusiveMinimum, int inlusiveMaximum) et l'utiliser dans tous les projets, mais peut-être qu'il existe déjà une méthode magique dans le cadre?

Si je devais le faire manuellement, quel serait le «meilleur» moyen (c'est-à-dire moins laid et plus rapide) de faire ce que je fais dans le premier exemple? Quelque chose comme ça?

public int LimitToRange(int value, int inclusiveMinimum, int inlusiveMaximum) 
{ 
    if (value >= inclusiveMinimum) 
    { 
     if (value <= inlusiveMaximum) 
     { 
      return value; 
     } 

     return inlusiveMaximum; 
    } 

    return inclusiveMinimum; 
} 
+7

Il est généralement appelé « pince »: voir http://stackoverflow.com/questions/516500/is- il-a-un-canonical-nom-pour-une-fonction-combinaison-min-et-max et http://stackoverflow.com/questions/427477/fastest-way-to-clamp-a-real-fixed-floating- point-value et http://stackoverflow.com/questions/2683442/where-can-i-find-the-clamp-function-in-net et ainsi de suite. – ShreevatsaR

Répondre

24

Je vois la réponse de Mark et relance par un this:

public static class InputExtensions 
{ 
    public static int LimitToRange(
     this int value, int inclusiveMinimum, int inclusiveMaximum) 
    { 
     if (value < inclusiveMinimum) { return inclusiveMinimum; } 
     if (value > inclusiveMaximum) { return inclusiveMaximum; } 
     return value; 
    } 
} 

Utilisation:

int userInput = ...; 

int result = userInput.LimitToRange(1, 5) 

Voir: Extension Methods

+4

S'il vous plaît noter que l'utilisation de méthodes d'extension ne doit pas nécessairement être une bonne chose. C'est juste un autre outil de programmation et il a ses usages. Placez-le au mauvais endroit ou contexte et vous finirez par écrire du code illisible. – Trap

7

Une autre façon d'écrire votre fonction LimitToRange est la suivante. Je pense que c'est un peu plus facile à comprendre tout en restant efficace.

+3

@MainMa: Si la valeur est dans la plage, vous devez effectuer deux vérifications, quel que soit l'ordre dans lequel vous avez placé vos instructions. Performance-sage toutes les réponses devraient être égales. – dtb

+1

En utilisant <= and > = peut éviter le deuxième contrôle ou un saut sur les cas de bord :-) – IamIC

42

Cette opération est appelée 'pince' et il est généralement écrit comme ceci:

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

J'aime le nom Clamp. Même le Microsoft.Xna.Framework a une méthode Clamp :) – Syd

+3

Clamp est le nom normal. Voir la spécification OpenCL. – IamIC

+7

Ps. Ce code est plus efficace avec <= and > =, plutôt que < and >. :-) – IamIC

3

Non, il n'y a pas de méthode pour celle construite dans le framework. Je suppose qu'il a été omis parce que vous avez déjà Min et Max, donc vous pouvez l'accomplir en les utilisant.

Si vous écrivez votre propre méthode, peu importe comment vous l'écrivez. Si vous utilisez des instructions if ou l'opérateur conditionnel ?, il compilera quand même à peu près le même code de toute façon.

+0

Cela ressemble à un argument pour cesser tous les efforts dans de nouveaux langages de programmation et/ou de mise à jour si nous "avons déjà [les outils]" !! ... nous avions les outils pour écrire nos propres fonctions Min Max alors pourquoi nous ont-ils donné ces ?? – noelicus

2

J'aime la réponse de Guffa, mais je suis surpris que personne n'ait encore posté une solution utilisant Min/Max.

public int LimitInclusive(int value, int min, int max) 
{ 
    return Math.Min(max, Math.Max(value, min)); 
} 
+0

Peut-être que l'utilisation de 'Min' et' Max' à chaque fois aurait un impact sur les performances? –

20

Une grande méthode plus propre qui fonctionnera avec plus que des nombres entiers (pris de ma propre bibliothèque de code partagé):

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

    return value; 
} 
+8

Cela déclenche une exception NullReferenceException si 'value' est' null'. Mieux vaut utiliser un IComparer (par exemple 'Comparer .Default') à la place. http://msdn.microsoft.com/en-us/library/azhsac5f.aspx – dtb

3

Pour serrer les valeurs sans donner aux utilisateurs tous les commentaires que la valeur entrée par ils ont tort, en général, peut-être pas une bonne idée (à mon humble avis). Cela peut conduire à des bogues subtils plus tard, qui sont difficiles à déboguer, en particulier lorsque les valeurs min/max sont déterminées au moment de l'exécution.

Pensez-y. Vous avez 100 $ dans votre compte bancaire, et vous voulez transférer 150 $ à votre ami. Souhaitez-vous que votre système bancaire lance un InsufficientFundsException ou discute avec votre ami (e) que vous avez transféré 150 $ mais qu'il n'a reçu que 100 $ (en supposant que la banque a viré le virement à 100 puisque vous n'aviez pas suffisamment de fonds)

étant dit, vous devriez également regarder les contrats de code.

public void MyFunction (Type input) 
{ 
    Contract.Requires(input > SomeReferenceValue); 
    Contract.Requires (input < SomeOtherReferencValue); 

} 

Cela forcera l'entrée de l'utilisateur à se situer dans la plage.

1

J'aime le nom Clamp. Je suggère la classe suivante

public class MathHelper 
{ 
    public static int Clamp (int value,int min,int max) 
    { 
      // todo - implementation 
    } 
    public static float Clamp (float value,float min,float max) 
    { 
      // todo - implementation 
    } 
) 

ou si vous voulez utiliser les médicaments génériques, alors

public class MathHelper 
{ 
    public static T Clamp<T> (T value, T min, T max) where T : IComparable 
    { 
     // todo - implementation 
     T output = value; 
     if (value.CompareTo(max) > 0) 
     { 
      return max; 
     } 
     if (value.CompareTo(min) < 0) 
     { 
      return min; 
     } 
     return output; 
    } 
} 
Questions connexes