我需要存储一个整数范围。 C#4.0中是否存在现有类型?
当然,我可以使用int From
和int To
属性编写自己的类,并构建适当的逻辑以确保From <= To
。但如果某种类型已存在,我当然更愿意使用它。
答案 0 :(得分:125)
我觉得最好自己动手。有些人使用Tuple
或Point
,但最后您希望Range
更广泛,并提供一些与Range
相关的方便方法。如果你需要一系列Double
s或一些自定义类,那么它也是最好的通道(例如:
/// <summary>The Range class.</summary>
/// <typeparam name="T">Generic parameter.</typeparam>
public class Range<T> where T : IComparable<T>
{
/// <summary>Minimum value of the range.</summary>
public T Minimum { get; set; }
/// <summary>Maximum value of the range.</summary>
public T Maximum { get; set; }
/// <summary>Presents the Range in readable format.</summary>
/// <returns>String representation of the Range</returns>
public override string ToString()
{
return string.Format("[{0} - {1}]", this.Minimum, this.Maximum);
}
/// <summary>Determines if the range is valid.</summary>
/// <returns>True if range is valid, else false</returns>
public bool IsValid()
{
return this.Minimum.CompareTo(this.Maximum) <= 0;
}
/// <summary>Determines if the provided value is inside the range.</summary>
/// <param name="value">The value to test</param>
/// <returns>True if the value is inside Range, else false</returns>
public bool ContainsValue(T value)
{
return (this.Minimum.CompareTo(value) <= 0) && (value.CompareTo(this.Maximum) <= 0);
}
/// <summary>Determines if this Range is inside the bounds of another range.</summary>
/// <param name="Range">The parent range to test on</param>
/// <returns>True if range is inclusive, else false</returns>
public bool IsInsideRange(Range<T> range)
{
return this.IsValid() && range.IsValid() && range.ContainsValue(this.Minimum) && range.ContainsValue(this.Maximum);
}
/// <summary>Determines if another range is inside the bounds of this range.</summary>
/// <param name="Range">The child range to test</param>
/// <returns>True if range is inside, else false</returns>
public bool ContainsRange(Range<T> range)
{
return this.IsValid() && range.IsValid() && this.ContainsValue(range.Minimum) && this.ContainsValue(range.Maximum);
}
}
答案 1 :(得分:8)
我写的一个小班可能对某人有帮助:
public class Range
{
public static List<int> range(int a, int b)
{
List<int> result = new List<int>();
for(int i = a; i <= b; i++)
{
result.Add(i);
}
return result;
}
public static int[] Understand(string input)
{
return understand(input).ToArray();
}
public static List<int> understand(string input)
{
List<int> result = new List<int>();
string[] lines = input.Split(new char[] {';', ','});
foreach (string line in lines)
{
try
{
int temp = Int32.Parse(line);
result.Add(temp);
}
catch
{
string[] temp = line.Split(new char[] { '-' });
int a = Int32.Parse(temp[0]);
int b = Int32.Parse(temp[1]);
result.AddRange(range(a, b).AsEnumerable());
}
}
return result;
}
}
然后你打电话:
Range.understand("1,5-9,14;16,17;20-24")
结果如下:
List<int>
[0]: 1
[1]: 5
[2]: 6
[3]: 7
[4]: 8
[5]: 9
[6]: 14
[7]: 16
[8]: 17
[9]: 20
[10]: 21
[11]: 22
[12]: 23
[13]: 24
答案 2 :(得分:3)
范围和索引随C#8.0一起发布。
您现在可以做到
string[] names =
{
"Archimedes", "Pythagoras", "Euclid", "Socrates", "Plato"
};
foreach (var name in names[1..4])
{
yield return name;
}
查看https://blogs.msdn.microsoft.com/dotnet/2018/12/05/take-c-8-0-for-a-spin/了解更多详情。
答案 3 :(得分:2)
编写像这样的扩展方法
public static class NumericExtentions
{
public static bool InRange(this int value, int from, int to)
{
if (value >= from && value <= to)
return true;
return false;
}
public static bool InRange(this double value, double from, double to)
{
if (value >= from && value <= to)
return true;
return false;
}
}
然后优雅地使用它
if (age.InRange(18, 39))
{
//Logic
}
答案 4 :(得分:1)
这个实现受@drharris的回答启发,允许您使用值来定义适当的数学区间,可以是包含/独占。
/// <summary>The Interval class.</summary>
/// <typeparam name="T">Generic parameter.</typeparam>
public class Interval<T> : IEquatable<Interval<T>>
where T : IComparable<T>, IEquatable<T>
{
public Interval()
{ }
public Interval(IntervalValue<T> minimum, IntervalValue<T> maximum)
{
this.Minimum = minimum;
this.Maximum = maximum;
}
/// <summary>Minimum value of the interval.</summary>
public IntervalValue<T>? Minimum { get; set; }
/// <summary>Maximum value of the interval.</summary>
public IntervalValue<T>? Maximum { get; set; }
/// <summary>Presents the Interval in readable format.</summary>
/// <returns>String representation of the Interval</returns>
public override string ToString()
{
var min = this.Minimum;
var max = this.Maximum;
var sb = new StringBuilder();
if (min.HasValue)
sb.AppendFormat(min.Value.ToString(IntervalNotationPosition.Left));
else
sb.Append("(-∞");
sb.Append(',');
if (max.HasValue)
sb.AppendFormat(max.Value.ToString(IntervalNotationPosition.Right));
else
sb.Append("∞)");
var result = sb.ToString();
return result;
}
/// <summary>Determines if the interval is valid.</summary>
/// <returns>True if interval is valid, else false</returns>
public bool IsValid()
{
var min = this.Minimum;
var max = this.Maximum;
if (min.HasValue && max.HasValue)
return min.Value.Value.CompareTo(max.Value.Value) <= 0;
return true;
}
/// <summary>Determines if the provided value is inside the interval.</summary>
/// <param name="x">The value to test</param>
/// <returns>True if the value is inside Interval, else false</returns>
public bool ContainsValue(T x)
{
if (x == null)
throw new ArgumentNullException(nameof(x));
var min = this.Minimum;
var max = this.Maximum;
var isValid = this.IsValid();
if (!isValid)
throw new InvalidOperationException("Interval is not valid.");
bool result = true; // (-∞,∞)
if (min.HasValue)
{
if (min.Value.Type == IntervalValueType.Exclusive)
result &= min.Value.Value.CompareTo(x) < 0;
else if (min.Value.Type == IntervalValueType.Inclusive)
result &= min.Value.Value.CompareTo(x) <= 0;
else
throw new NotSupportedException();
}
if (max.HasValue)
{
if (max.Value.Type == IntervalValueType.Exclusive)
result &= max.Value.Value.CompareTo(x) > 0;
else if (max.Value.Type == IntervalValueType.Inclusive)
result &= max.Value.Value.CompareTo(x) >= 0;
else
throw new NotSupportedException();
}
return result;
}
public bool Equals(Interval<T> other)
{
if (other == null)
return false;
if (ReferenceEquals(this, other))
return true;
return this.Minimum?.Equals(other.Minimum) == true
&& this.Maximum?.Equals(other.Maximum) == true;
}
public override bool Equals(object obj)
{
return this.Equals(obj as Interval<T>);
}
public override int GetHashCode()
{
unchecked
{
int hash = (int)2166136261;
hash = hash * 16777619 ^ this.Minimum?.GetHashCode() ?? 0;
hash = hash * 16777619 ^ this.Maximum?.GetHashCode() ?? 0;
return hash;
}
}
}
public struct IntervalValue<T> : IEquatable<IntervalValue<T>>
where T : IComparable<T>, IEquatable<T> //, IFormattable
{
private readonly T value;
private readonly IntervalValueType type;
public IntervalValue(T value, IntervalValueType type)
{
if (value == null)
throw new ArgumentNullException(nameof(value));
this.value = value;
this.type = type;
}
public T Value
{
get { return this.value; }
}
public IntervalValueType Type
{
get { return this.type; }
}
public bool Equals(IntervalValue<T> other)
{
return this.value.Equals(other.value)
&& this.type == other.type;
}
public override bool Equals(object obj)
{
return obj is IntervalValue<T> && this.Equals((IntervalValue<T>)obj);
}
public override int GetHashCode()
{
unchecked
{
int hash = (int)2166136261;
hash = hash * 16777619 ^ this.value.GetHashCode();
hash = hash * 16777619 ^ this.type.GetHashCode();
return hash;
}
}
internal string ToString(IntervalNotationPosition position)
{
var notation = this.Type.ToString(position);
switch (position)
{
case IntervalNotationPosition.Left:
return string.Format("{0}{1}", notation, this.Value);
case IntervalNotationPosition.Right:
return string.Format("{0}{1}", this.Value, notation);
default:
throw new NotSupportedException();
}
}
}
internal static class IntervalValueTypeExtensions
{
public static string ToString(this IntervalValueType type, IntervalNotationPosition position)
{
switch (position)
{
case IntervalNotationPosition.Left:
switch (type)
{
case IntervalValueType.Inclusive: return "[";
case IntervalValueType.Exclusive: return "(";
default:
throw new NotSupportedException();
}
case IntervalNotationPosition.Right:
switch (type)
{
case IntervalValueType.Inclusive: return "]";
case IntervalValueType.Exclusive: return ")";
default:
throw new NotSupportedException();
}
break;
default:
throw new NotSupportedException();
}
}
}
public enum IntervalValueType
{
Inclusive,
Exclusive
}
public enum IntervalNotationPosition
{
Left,
Right
}
答案 5 :(得分:1)
有一个Enumerable.Range
方法,但是该方法接受start
和count
作为其参数。为了使它像您想要的start
和end
一样工作,end
的公式应为end - start + 1
用法:
Enumerable.Range(start, end - start + 1).ToList()
答案 6 :(得分:0)
由于我也错过了C#中的间隔,我implemented a fully generic Interval class甚至可以处理更复杂类型的间隔,例如两个DateTime
之间的间隔,在计算过程中涉及TimeSpan
。
示例用例,其中GUI元素表示时间间隔:
// Mockup of a GUI element and mouse position.
var timeBar = new { X = 100, Width = 200 };
int mouseX = 180;
// Find out which date on the time bar the mouse is positioned on,
// assuming it represents whole of 2014.
var timeRepresentation = new Interval<int>( timeBar.X, timeBar.X + timeBar.Width );
DateTime start = new DateTime( 2014, 1, 1 );
DateTime end = new DateTime( 2014, 12, 31 );
var thisYear = new Interval<DateTime, TimeSpan>( start, end );
DateTime hoverOver = timeRepresentation.Map( mouseX, thisYear );
// If the user clicks, zoom in to this position.
double zoomLevel = 0.5;
double zoomInAt = thisYear.GetPercentageFor( hoverOver );
Interval<DateTime, TimeSpan> zoomed = thisYear.Scale( zoomLevel, zoomInAt );
// Iterate over the interval, e.g. draw labels.
zoomed.EveryStepOf( TimeSpan.FromDays( 1 ), d => DrawLabel( d ) );
更广泛地表示支持的功能check out the unit tests。
隐藏它uses expression trees to compile type operator operations at runtime,它们被缓存,因此第一次初始化类型时只有一个成本。
答案 7 :(得分:0)
改进@ andrius-naruševičius非常有用的答案,使其更具惯用性和易于定制
edt_dailNumber.setInputType(InputType.TYPE_NULL);
和测试:
/// <summary>
/// http://stackoverflow.com/questions/5343006/is-there-a-c-sharp-type-for-representing-an-integer-range
/// </summary>
public class Range
{
readonly static char[] Separators = {','};
public static List<int> Explode(int from, int to)
{
return Enumerable.Range(from, (to-from)+1).ToList();
}
public static List<int> Interpret(string input)
{
var result = new List<int>();
var values = input.Split(Separators);
string rangePattern = @"(?<range>(?<from>\d+)-(?<to>\d+))";
var regex = new Regex(rangePattern);
foreach (string value in values)
{
var match = regex.Match(value);
if (match.Success)
{
var from = Parse(match.Groups["from"].Value);
var to = Parse(match.Groups["to"].Value);
result.AddRange(Explode(from, to));
}
else
{
result.Add(Parse(value));
}
}
return result;
}
/// <summary>
/// Split this out to allow custom throw etc
/// </summary>
private static int Parse(string value)
{
int output;
var ok = int.TryParse(value, out output);
if (!ok) throw new FormatException($"Failed to parse '{value}' as an integer");
return output;
}
}
答案 8 :(得分:-1)
此外,这里的切线有些不同,但有时范围仅对迭代它们有用,就像在python中习惯那样。在这种情况下,System.Linq
命名空间定义了一个static IEnumerable<int> Range(Int32, Int32)
方法,如签名所示,
生成指定范围内的整数序列
答案 9 :(得分:-3)
struct怎么样?