How to elegantly check if a number is within a range?

后端 未结 27 1935
挽巷
挽巷 2020-11-27 11:17

How can I do this elegantly with C# and .NET 3.5/4?

For example, a number can be between 1 and 100.

I know a simple if would suffice; but the keyword to this

相关标签:
27条回答
  • 2020-11-27 11:47

    With a bit of extension method abuse, we can get the following "elegant" solution:

    using System;
    
    namespace Elegant {
        public class Range {
            public int Lower { get; set; }
            public int Upper { get; set; }
        }
    
        public static class Ext {
            public static Range To(this int lower, int upper) {
                return new Range { Lower = lower, Upper = upper };
            }
    
            public static bool In(this int n, Range r) {
                return n >= r.Lower && n <= r.Upper;
            }
        }
    
        class Program {
            static void Main() {
                int x = 55;
                if (x.In(1.To(100)))
                    Console.WriteLine("it's in range! elegantly!");
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-27 11:48

    There are a lot of options:

    int x = 30;
    if (Enumerable.Range(1,100).Contains(x))
        //true
    
    if (x >= 1 && x <= 100)
        //true
    

    Also, check out this SO post for regex options.

    0 讨论(0)
  • 2020-11-27 11:48

    Using an && expression to join two comparisons is simply the most elegant way to do this. If you try using fancy extension methods and such, you run into the question of whether to include the upper bound, the lower bound, or both. Once you start adding additional variables or changing the extension names to indicate what is included, your code becomes longer and harder to read (for the vast majority of programmers). Furthermore, tools like Resharper will warn you if your comparison doesn't make sense (number > 100 && number < 1), which they won't do if you use a method ('i.IsBetween(100, 1)').

    The only other comment I'd make is that if you're checking inputs with the intention to throw an exception, you should consider using code contracts:

    Contract.Requires(number > 1 && number < 100)
    

    This is more elegant than if(...) throw new Exception(...), and you could even get compile-time warnings if someone tries to call your method without ensuring that the number is in bounds first.

    0 讨论(0)
  • 2020-11-27 11:48

    Elegant because it doesn't require you to determine which of the two boundary values is greater first. It also contains no branches.

    public static bool InRange(float val, float a, float b)
    {
        // Determine if val lies between a and b without first asking which is larger (a or b)
        return ( a <= val & val < b ) | ( b <= val & val < a );
    }
    
    0 讨论(0)
  • 2020-11-27 11:50

    How about something like this?

    if (theNumber.isBetween(low, high, IntEx.Bounds.INCLUSIVE_INCLUSIVE))
    {
    }
    

    with the extension method as follows (tested):

    public static class IntEx
    {
        public enum Bounds 
        {
            INCLUSIVE_INCLUSIVE, 
            INCLUSIVE_EXCLUSIVE, 
            EXCLUSIVE_INCLUSIVE, 
            EXCLUSIVE_EXCLUSIVE
        }
    
        public static bool isBetween(this int theNumber, int low, int high, Bounds boundDef)
        {
            bool result;
            switch (boundDef)
            {
                case Bounds.INCLUSIVE_INCLUSIVE:
                    result = ((low <= theNumber) && (theNumber <= high));
                    break;
                case Bounds.INCLUSIVE_EXCLUSIVE:
                    result = ((low <= theNumber) && (theNumber < high));
                    break;
                case Bounds.EXCLUSIVE_INCLUSIVE:
                    result = ((low < theNumber) && (theNumber <= high));
                    break;
                case Bounds.EXCLUSIVE_EXCLUSIVE:
                    result = ((low < theNumber) && (theNumber < high));
                    break;
                default:
                    throw new System.ArgumentException("Invalid boundary definition argument");
            }
            return result;
        }
    }
    
    0 讨论(0)
  • 2020-11-27 11:52

    In production code I would simply write

    1 <= x && x <= 100
    

    This is easy to understand and very readable.

    Starting with C#9.0 we can write

    x is >= 1 and <= 100   // Note that we have to write x only once.
                           // "is" introduces a pattern matching expression.
                           // "and" is part of the pattern matching unlike the logical "&&".
                           // With "&&" we would have to write: x is >= 1 && x is <= 100
    

    Here is a clever method that reduces the number of comparisons from two to one by using some math. The idea is that one of the two factors becomes negative if the number lies outside of the range and zero if the number is equal to one of the bounds:

    If the bounds are inclusive:

    (x - 1) * (100 - x) >= 0
    

    or

    (x - min) * (max - x) >= 0
    

    If the bounds are exclusive:

    (x - 1) * (100 - x) > 0
    

    or

    (x - min) * (max - x) > 0
    
    0 讨论(0)
提交回复
热议问题