정수 범위를 나타내는 C # 유형이 있습니까?


105

정수 범위를 저장할 필요가 있습니다. C # 4.0에 기존 유형이 있습니까?

물론 int Fromint To속성을 사용 하여 고유 한 클래스를 작성 하고 From <= To. 그러나 유형이 이미 존재하는 경우에는 당연히 그것을 사용합니다.


2
받아 들여진 대답을 바꿔야한다고 생각합니다. @rsenna의 대답 Enumerable.Range은 내가 본 것에서 C # 3.0에서 범위를 구현하는 사실상의 방법입니다. 그리고 C # 3.0은 2007 년부터 출시되었으므로 현재 9 년이되었습니다.
Ehtesh Choudhury 2010

@EhteshChoudhury OP는 이산 값을 처리하거나 최소 및 최대에 대해 해당 유형의 유효성을 검사하는 것에 대해 언급하지 않습니다 Enumerable.Range. OP는 단순히 하한 및 상한의 속성을 가질 수있는 Intervals를 처리하는 기존 데이터 구조를 찾고있었습니다 (특정 동작을 강제하는 메서드 외에).
Sunny Patel

2
@EhteshChoudhury "Do n't use Enumerable.Range"코러스에 내 목소리를 추가하겠습니다. 이는 한 쌍의 최소 / 최대 값을 유지하는 간단한 문제를 해결하는 끔찍한 방법입니다. Enumerable.Max (IEnumerable <int>) (또는 Enumerable.Min)에 대한 모든 호출은 전체 범위를 반복하여 경계를 파악합니다. 다른 사람들이 말했듯이, 그것은 많은 반복 일 수 있습니다 . 우리는 여기서 마이크로 성능 튜닝을 말하는 것이 아니라 심각한 속도 저하를 말하는 것입니다. 그런 종류의 프로그래밍이 .Net이 성능에 대해 (부당하게) 나쁜 이름을 얻는 이유입니다! 허용되는 답변과 유사한 답변이 유일한 실용적인 솔루션입니다.
다니엘 스콧

1
공정 해. Enumerable.Range(1,1000000)에 대한 범위 데이터 유형에 비해 (1,1000000)에 대해 훨씬 더 많은 공간을 차지합니다. 나는 질문 잘못된에게이 질문을 받았다 처음 읽고 그것을 요구라고 생각Enumerable.Range
Ehtesh Choudhury의

1
@EhteshChoudhury Enumerable.Range는 적어도 ToList () 메서드를 호출하는 데 1,1000000을 사용하지 않습니다.
Luis

답변:


136

나는 내 자신을 굴리는 것이 가장 좋다는 것을 알았다. 어떤 사람들은 Tuples 또는 Points를 사용하지만 결국에는 Range광범위하고 Range. 일반적인 경우에도 가장 좋습니다 ( Doubles 범위 또는 일부 사용자 지정 클래스 범위가 필요한 경우 어떻게 됩니까?). 예 :

/// <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);
    }
}

13
당시에는 IComparable이것이 연산자 과부하를 보장 할 수 있을지 확신하지 못했습니다 . 나는 CompareTo방법, 따라서 그 사용법을 보장받습니다 .
drharris 2011 년

14
: I 밝혀 바로 것을하고 있었다 stackoverflow.com/questions/5101378/...이
drharris

2
struct대신 더 나을 수 class있습니까?
xmedeko 2015

2
@Will Hey man은 barebones 문서의 지혜를 두드리지 않습니다. :)
drharris

7
매우 도움이되었습니다, 감사합니다! 즉시 초기화 할 수있는 생성자를 추가하지 않겠습니까? public Range(T min, T max) { Minimum = min; Maximum = max; }
NateJC

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

4
i를 기능을 이해 정말 좋아
드래곤 본

understand기능은 멋지다. 우리 코드의 리뷰에서 이것을 본다면 (나를 위해) 계속 들리기 Set때문에 이름을로 바꾸는 것이 좋습니다 Range.
dlw

7

범위 및 인덱스는 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/ 을 확인 하세요.


이것은 다른 개념이며 원하는 OP가 아닙니다.
PepitoSh

그러나 System.Range유형은 만 허용 int합니다.
snipsnipsnip

.NET Core 3.0 이후로만 사용할 수 있다는 점을 언급하는 것이 중요합니다. .NET Framework에서는 사용할 수 없습니다.
Sarrus

2

@ andrius-naruševičius를 개선하면 좀 더 관용적이고 쉽게 사용자 정의 할 수있는 매우 유용한 답변입니다.

/// <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;
    }
}

및 테스트 :

    [Test]
    public void ExplodeRange()
    {
        var output = Range.Explode(5, 9);

        Assert.AreEqual(5, output.Count);
        Assert.AreEqual(5, output[0]);
        Assert.AreEqual(6, output[1]);
        Assert.AreEqual(7, output[2]);
        Assert.AreEqual(8, output[3]);
        Assert.AreEqual(9, output[4]);
    }

    [Test]
    public void ExplodeSingle()
    {
        var output = Range.Explode(1, 1);

        Assert.AreEqual(1, output.Count);
        Assert.AreEqual(1, output[0]);
    }

    [Test]
    public void InterpretSimple()
    {
        var output = Range.Interpret("50");
        Assert.AreEqual(1, output.Count);
        Assert.AreEqual(50, output[0]);
    }

    [Test]
    public void InterpretComplex()
    {
        var output = Range.Interpret("1,5-9,14,16,17,20-24");

        Assert.AreEqual(14, output.Count);
        Assert.AreEqual(1, output[0]);
        Assert.AreEqual(5, output[1]);
        Assert.AreEqual(6, output[2]);
        Assert.AreEqual(7, output[3]);
        Assert.AreEqual(8, output[4]);
        Assert.AreEqual(9, output[5]);
        Assert.AreEqual(14, output[6]);
        Assert.AreEqual(16, output[7]);
        Assert.AreEqual(17, output[8]);
        Assert.AreEqual(20, output[9]);
        Assert.AreEqual(21, output[10]);
        Assert.AreEqual(22, output[11]);
        Assert.AreEqual(23, output[12]);
        Assert.AreEqual(24, output[13]);
    }

    [ExpectedException(typeof (FormatException))]
    [Test]
    public void InterpretBad()
    {
        Range.Interpret("powdered toast man");
    }

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
}

1
if (value >= from && value <= to) return true;하지 return (value >= from && value <= to)?
sdgfsdh

2

@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
}

1

또한 여기에 약간 다른 탄젠트가 있지만 때로는 범위가 파이썬에서 수행하는 관례적인 것처럼 반복하는 데에만 유용합니다. 이 경우 System.Linq네임 스페이스는 static IEnumerable<int> Range(Int32, Int32)서명에서 알 수 있듯이 다음과 같은 메서드를 정의 합니다.

지정된 범위 내에서 일련의 정수를 생성합니다.

MSDN의 설명서 및 예제를 참조하십시오.


@Seth Enumerable 범위는 시작과 카운트를 허용하므로 안됩니다.
Rashid

@Rashid는 충분히 공평하지만 실제로 문제라고 생각하지 않습니다. 나는 OP는 요청 알고있다 "로 클래스 int Fromint To"하지만 나는이 자신의 포인트를 설명하기 위해 특정 요구 사항이 아니라 뭔가 아니라는 것을 꽤 확신한다. 누구든지 관심이 들어, 물론, 값 count매개 변수는 매우 쉽게 가지고 부여, 판단 할 수 int Fromint To이미 변수.
Gaboik1

1

거기입니다 Enumerable.Range방법이 있지만이 하나 수용 startcount그 매개 변수로. 위해서는 당신이 원하는대로 작동하려면 startendend공식 것end - start + 1

용법:

Enumerable.Range(start, end - start + 1).ToList()


0

또한 C #에서 간격이 누락 되었기 때문에 계산 중에 ' 가 포함 된 두 개의 간격과 같이 더 복잡한 형식의 간격을 처리 할 수 있는 완전히 일반적인 Interval 클래스구현했습니다 .DateTimeTimeSpan

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 ) );

지원되는 기능에 대한보다 광범위한 표현은 단위 테스트를 확인하십시오 .

내부적으로 는 표현식 트리를 사용하여 런타임에 유형 연산자 작업을 컴파일 하는데, 이는 캐시에 저장되므로 유형이 처음 초기화 될 때만 비용이 발생합니다.


-2

방법에 대한 구조체 ?


2
최소 및 최대 값이 인스턴스화되면 절대 변경 되지 않는 한 구조체를 사용하지 않습니다 .
IAbstract 2011 년

"이미 존재한다"는 질문에 대답합니다. 구조체 유형은 문제의 유형과 같은 유형에 대해 특별히 존재합니다.
James Sumners 2011 년

3
Struct는 그 자체로 유형이 아닙니다. 합법적 인 대답 - 당신은 기본적으로 "더 자신의 유형 만들 가서"말을하지 않습니다
로버트 레비

2
처음 세 단어 링크 된 문서에서 당신과 함께 동의 "구조체 형식을 ..."
제임스 Sumners 보낸에게

나는 jsumners에 동의하는 경향이 있습니다. 구조체는이 작업을 수행하는 쉽고 간단한 방법이며 이러한 간단한 문제를 위해 나중에 유지 관리해야하는 2 페이지의 코드를 생성하지 않습니다. 같은 일에 Pair <...>를 사용할 수도 있습니다. 마이크로 소프트는 다양한 장소에서 불변하는 구조체에 대한 그들 자신의 "규칙"을 깨뜨립니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.