我如何用c#优雅地做到这一点?
例如,一个数字可以是1到100之间。
我知道一个简单的if (x >= 1 && x <= 100)就足够了;但是有很多语法糖和新特性不断添加到c# /。Net这个问题是关于更习惯的(一个可以称之为优雅的)写法。
性能不是问题,但请在非O(1)的解决方案中添加性能说明,因为人们可能会复制粘贴建议。
我如何用c#优雅地做到这一点?
例如,一个数字可以是1到100之间。
我知道一个简单的if (x >= 1 && x <= 100)就足够了;但是有很多语法糖和新特性不断添加到c# /。Net这个问题是关于更习惯的(一个可以称之为优雅的)写法。
性能不是问题,但请在非O(1)的解决方案中添加性能说明,因为人们可能会复制粘贴建议。
当前回答
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)').
我要做的唯一另一个评论是,如果你检查输入的意图是抛出异常,你应该考虑使用代码契约:
Contract.Requires(number > 1 && number < 100)
这比if(…)抛出new Exception(…)更优雅,如果有人试图调用您的方法而没有首先确保该数字在边界内,您甚至可以得到编译时警告。
其他回答
我会创建一个Range对象,就像这样:
public class Range<T> where T : IComparable
{
public T InferiorBoundary{get;private set;}
public T SuperiorBoundary{get;private set;}
public Range(T inferiorBoundary, T superiorBoundary)
{
InferiorBoundary = inferiorBoundary;
SuperiorBoundary = superiorBoundary;
}
public bool IsWithinBoundaries(T value){
return InferiorBoundary.CompareTo(value) > 0 && SuperiorBoundary.CompareTo(value) < 0;
}
}
那么你可以这样使用它:
Range<int> myRange = new Range<int>(1,999);
bool isWithinRange = myRange.IsWithinBoundaries(3);
这样你就可以在其他类型中重用它。
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 must write x only once. is introduces a pattern matching expression where and is part of the pattern. && would require us to repeat x is as in 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. There is not necessarily a performance advantage in doing so, but it is elegant. 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
使用内置的Range结构体(c# 8+),我们可以创建一个扩展方法来检查索引是否在原始范围内。
public static bool IsInRangeOf(this Range range, Index index)
{
return index.Value >= range.Start.Value && index.Value < range.End.Value;
}
由于Index覆盖隐式操作符,因此可以传递int型而不是Index结构体。
var range = new Range(1, 10);
var isInRange = range.IsInRangeOf(1); // true, 1..10 is inclusive min range index(1)
var isInRange = range.IsInRangeOf(10); // false, 1..10 exclusive on max range index (10).
var isInRange = range.IsInRangeOf(100); // false
就像其他人说的,使用简单的if。
你应该考虑一下顺序。
e.g
1 <= x && x <= 100
容易读吗
x >= 1 && x <= 100
当检查一个“数字”是否在一个范围内时,你必须清楚你的意思,两个数字相等意味着什么?一般来说,你应该把所有浮点数包装在一个所谓的“epsilon球”中,这是通过选择一个小的值来完成的,如果两个值如此接近,它们就是相同的。
private double _epsilon = 10E-9;
/// <summary>
/// Checks if the distance between two doubles is within an epsilon.
/// In general this should be used for determining equality between doubles.
/// </summary>
/// <param name="x0">The orgin of intrest</param>
/// <param name="x"> The point of intrest</param>
/// <param name="epsilon">The minimum distance between the points</param>
/// <returns>Returns true iff x in (x0-epsilon, x0+epsilon)</returns>
public static bool IsInNeghborhood(double x0, double x, double epsilon) => Abs(x0 - x) < epsilon;
public static bool AreEqual(double v0, double v1) => IsInNeghborhood(v0, v1, _epsilon);
有了这两个辅助,并假设任何数字都可以转换为double而不需要所需的精度。现在需要的是一个枚举和另一个方法
public enum BoundType
{
Open,
Closed,
OpenClosed,
ClosedOpen
}
另一种方法如下:
public static bool InRange(double value, double upperBound, double lowerBound, BoundType bound = BoundType.Open)
{
bool inside = value < upperBound && value > lowerBound;
switch (bound)
{
case BoundType.Open:
return inside;
case BoundType.Closed:
return inside || AreEqual(value, upperBound) || AreEqual(value, lowerBound);
case BoundType.OpenClosed:
return inside || AreEqual(value, upperBound);
case BoundType.ClosedOpen:
return inside || AreEqual(value, lowerBound);
default:
throw new System.NotImplementedException("You forgot to do something");
}
}
现在,这可能远远超过了您想要的,但它使您不必一直处理舍入问题,并试图记住一个值是否被舍入到哪个位置。如果你需要,你可以很容易地将它扩展到任意的情况并允许变化。