我如何用c#优雅地做到这一点?
例如,一个数字可以是1到100之间。
我知道一个简单的if (x >= 1 && x <= 100)就足够了;但是有很多语法糖和新特性不断添加到c# /。Net这个问题是关于更习惯的(一个可以称之为优雅的)写法。
性能不是问题,但请在非O(1)的解决方案中添加性能说明,因为人们可能会复制粘贴建议。
我如何用c#优雅地做到这一点?
例如,一个数字可以是1到100之间。
我知道一个简单的if (x >= 1 && x <= 100)就足够了;但是有很多语法糖和新特性不断添加到c# /。Net这个问题是关于更习惯的(一个可以称之为优雅的)写法。
性能不是问题,但请在非O(1)的解决方案中添加性能说明,因为人们可能会复制粘贴建议。
当前回答
In C, if time efficiency is crucial and integer overflows will wrap, one could do if ((unsigned)(value-min) <= (max-min)) .... If 'max' and 'min' are independent variables, the extra subtraction for (max-min) will waste time, but if that expression can be precomputed at compile time, or if it can be computed once at run-time to test many numbers against the same range, the above expression may be computed efficiently even in the case where the value is within range (if a large fraction of values will be below the valid range, it may be faster to use if ((value >= min) && (value <= max)) ... because it will exit early if value is less than min).
不过,在使用这样的实现之前,请先对目标机器进行基准测试。在某些处理器上,由两部分组成的表达式可能在所有情况下都更快,因为两个比较可能是独立完成的,而在减法和比较方法中,减法必须在比较执行之前完成。
其他回答
如果是为了验证方法参数,没有一个解决方案会抛出argumentoutofranceexception,并允许简单/适当地配置包含/排除的最小/最大值。
像这样使用
public void Start(int pos)
{
pos.CheckRange(nameof(pos), min: 0);
if (pos.IsInRange(max: 100, maxInclusive: false))
{
// ...
}
}
我只是写出了这些漂亮的函数。它还具有对有效值没有分支(单个if)的优点。最难的部分是制作适当的异常消息。
/// <summary>
/// Returns whether specified value is in valid range.
/// </summary>
/// <typeparam name="T">The type of data to validate.</typeparam>
/// <param name="value">The value to validate.</param>
/// <param name="min">The minimum valid value.</param>
/// <param name="minInclusive">Whether the minimum value is valid.</param>
/// <param name="max">The maximum valid value.</param>
/// <param name="maxInclusive">Whether the maximum value is valid.</param>
/// <returns>Whether the value is within range.</returns>
public static bool IsInRange<T>(this T value, T? min = null, bool minInclusive = true, T? max = null, bool maxInclusive = true)
where T : struct, IComparable<T>
{
var minValid = min == null || (minInclusive && value.CompareTo(min.Value) >= 0) || (!minInclusive && value.CompareTo(min.Value) > 0);
var maxValid = max == null || (maxInclusive && value.CompareTo(max.Value) <= 0) || (!maxInclusive && value.CompareTo(max.Value) < 0);
return minValid && maxValid;
}
/// <summary>
/// Validates whether specified value is in valid range, and throws an exception if out of range.
/// </summary>
/// <typeparam name="T">The type of data to validate.</typeparam>
/// <param name="value">The value to validate.</param>
/// <param name="name">The name of the parameter.</param>
/// <param name="min">The minimum valid value.</param>
/// <param name="minInclusive">Whether the minimum value is valid.</param>
/// <param name="max">The maximum valid value.</param>
/// <param name="maxInclusive">Whether the maximum value is valid.</param>
/// <returns>The value if valid.</returns>
public static T CheckRange<T>(this T value, string name, T? min = null, bool minInclusive = true, T? max = null, bool maxInclusive = true)
where T : struct, IComparable<T>
{
if (!value.IsInRange(min, minInclusive, max, maxInclusive))
{
if (min.HasValue && minInclusive && max.HasValue && maxInclusive)
{
var message = "{0} must be between {1} and {2}.";
throw new ArgumentOutOfRangeException(name, value, message.FormatInvariant(name, min, max));
}
else
{
var messageMin = min.HasValue ? GetOpText(true, minInclusive).FormatInvariant(min) : null;
var messageMax = max.HasValue ? GetOpText(false, maxInclusive).FormatInvariant(max) : null;
var message = (messageMin != null && messageMax != null) ?
"{0} must be {1} and {2}." :
"{0} must be {1}.";
throw new ArgumentOutOfRangeException(name, value, message.FormatInvariant(name, messageMin ?? messageMax, messageMax));
}
}
return value;
}
private static string GetOpText(bool greaterThan, bool inclusive)
{
return (greaterThan && inclusive) ? "greater than or equal to {0}" :
greaterThan ? "greater than {0}" :
inclusive ? "less than or equal to {0}" :
"less than {0}";
}
public static string FormatInvariant(this string format, params object?[] args) => string.Format(CultureInfo.InvariantCulture, format, args);
static class ExtensionMethods
{
internal static bool IsBetween(this double number,double bound1, double bound2)
{
return Math.Min(bound1, bound2) <= number && number <= Math.Max(bound2, bound1);
}
internal static bool IsBetween(this int number, double bound1, double bound2)
{
return Math.Min(bound1, bound2) <= number && number <= Math.Max(bound2, bound1);
}
}
使用
double numberToBeChecked = 7;
var result = numberToBeChecked.IsBetween(100,122);
var result = 5.IsBetween(100,120);
var result = 8.0.IsBetween(1.2,9.6);
有很多选择:
int x = 30;
if (Enumerable.Range(1,100).Contains(x)) //true
实际上,基本的,如果更优雅的话,可以在第一张支票中用倒序写:
if (1 <= x && x <= 100) //true
此外,查看这篇SO帖子的正则表达式选项。
注:
LINQ solution is strictly for style points - since Contains iterates over all items its complexity is O(range_size) and not O(1) normally expected from a range check. More generic version for other ranges (notice that second argument is count, not end): if (Enumerable.Range(start, end - start + 1).Contains(x) There is temptation to write if solution without && like 1 <= x <= 100 - that look really elegant, but in C# leads to a syntax error "Operator '<=' cannot be applied to operands of type 'bool' and 'int'"
只是为了增加这里的噪音,你可以创建一个扩展方法:
public static bool IsWithin(this int value, int minimum, int maximum)
{
return value >= minimum && value <= maximum;
}
这样你就能做…
int val = 15;
bool foo = val.IsWithin(5,20);
话虽如此,当检查本身只有一行时,这样做似乎是一件愚蠢的事情。
您可以使用模式匹配以最优雅的方式实现这一点:
int i = 5;
if(i is (>0 and <=10))
{
}