HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Custom struct design: Range

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
customdesignstructrange

Problem

I had a need for some inventory management in a recent project. I decided to create a custom struct for managing the concept of a number range. It allows for easy navigation of a collection.

It function similar to .NET's own Enumeration classes, with extension methods, wrapper classes and so forth allowing for dealing with a IRanged as defined:

public interface IRanged
{
    RangeType RangeType { get; }
    T Start { get; }
    T End { get; }
    T Middle { get; }
    int Size { get; }
    T AtPercent(int percent);
    T ValueAtIndex(int index, IRangedEdgeStrategy strategy);
}


The actual core struct class is:

public struct Range : IEquatable, IRanged, IEnumerable
{
   public Range(int start, int end)
   {
        if (start.Equals(end)) throw new ArgumentException("Cannot create a range of size zero");

        if (start  (int)Math.Round(((decimal)Start + End) / 2);

    public int Size => Difference(Start, End);

    public bool Contains(Range range)
    {
        return Start = range.End;
    }

    public static int Difference(int a, int b)
    {
        return Math.Abs(a - b) + 1;
    }

    public int ValueAtIndex(int index, IRangedEdgeStrategy strategy)
    {
        return strategy.Handle(this, index);
    }

    public int AtPercent(int percent)
    {
        if (percent  100)
            throw new ArgumentOutOfRangeException("Percentage needs to be a integer value between 1 and 100");

        var p = percent/100f*Size;
        var clampedPercent = (int) Math.Round(p);
        return AsClamped(clampedPercent);
    }

    public int AsClamped(int index)
    {
        return AsClamped(index, Start, End);
    }

    public static int AsClamped(int index, int start, int end)
    {
        return index  end ? end : index;
    }

    #region IEnumerable

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public IEnumerator GetEnumerator()
    {
        for (var i = Start; i {End})"; }
}


A range lis

Solution

Only targeting public struct Range

-
If possible a struct should be immutable so you shouldn't let a user of this struct set the RangeType property.

-
pre calculate values in the constructor instead of calculating the values if accessed

public int Middle => (int)Math.Round(((decimal)Start + End) / 2);
public int Size => Difference(Start, End);


-
always use braces {} although they might be optional. Omitting braces can lead to serious bugs, using them will structure your code better and make you code more readable.

-
leave your operators some room to breathe.

var p = percent/100f*Size;


would be more readable like

var p = percent / 100f * Size;


-
a ternary inside a ternary expression becomes almost unreadable.

This

public static int AsClamped(int index, int start, int end)
{
    return index  end ? end : index;
}


would be more readable like

public static int AsClamped(int index, int start, int end)
{
    if (index  end ? end : index;
}

Code Snippets

public int Middle => (int)Math.Round(((decimal)Start + End) / 2);
public int Size => Difference(Start, End);
var p = percent/100f*Size;
var p = percent / 100f * Size;
public static int AsClamped(int index, int start, int end)
{
    return index < start ? start : index > end ? end : index;
}
public static int AsClamped(int index, int start, int end)
{
    if (index < start)
    {
        return start;
    }
    return index > end ? end : index;
}

Context

StackExchange Code Review Q#134816, answer score: 7

Revisions (0)

No revisions yet.