-
Notifications
You must be signed in to change notification settings - Fork 344
Added IsValid, Bind, and BindToValid. Renamed Last to End #1981
Added IsValid, Bind, and BindToValid. Renamed Last to End #1981
Conversation
} | ||
|
||
public bool IsBound => First != UnboundedFirst && Last != UnboundedLast; | ||
public bool IsBound => First != UnboundedFirst && End != UnboundedEnd; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why can't First == UnboundedFirst? I thought only End is exclusive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The property returns true if both of the ends are bound. Otherwise false. The property can be used to see if some operations on the range would work, e.g. GetEnumerator works only for bound ranges. "Unbound" means "I don't know/care what the value is". It does not make sense to enumerate such ranges.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. So, you can't enumerate from [int.MinValue..int.MinValue + 5] similar to int.MaxValue?
int.MaxValue being the sentinel for End makes sense given End is exclusive (for example [0..int.MaxValue] works fine. Are we treating the lower bound the same way?
} | ||
public static Range Construct(int first, int last) | ||
=> new Range(first, last); | ||
public static Range Construct(int first, int end) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: add new line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will do
else // First is bounded | ||
{ | ||
if (First < 0) return false; | ||
if (End == UnboundedEnd) return First <= length; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should First be < length here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made it so that range can start after the last element. This is to allow empty ranges.
} | ||
|
||
/// <summary> | ||
/// Returns true if this Range is a valid range for a zero based index of sspecified length. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: spelling - sspecified
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will fix. thanks
return End <= length; | ||
} | ||
|
||
throw new NotImplementedException(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will we ever reach here? It doesn't look like it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nah, I will remove.
throw new NotImplementedException(); | ||
} | ||
|
||
public Range Bind(int length) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please add a comment here explaining what Bind is meant to do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will do
@@ -106,7 +141,7 @@ public void Errors() | |||
}); | |||
Assert.Throws<ArgumentOutOfRangeException>(() => | |||
{ | |||
// MaxValue is used as a sentinel for Last, so Last cannot endup being it. | |||
// MaxValue is used as a sentinel for End, so End cannot endup being it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: spelling endup
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will fix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
otherwise, LGTM.
{ | ||
public const int UnboundedFirst = Int32.MinValue; | ||
public const int UnboundedLast = Int32.MaxValue; | ||
public const int UnboundedEnd = Int32.MaxValue; | ||
|
||
public readonly int First; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First
should be renamed to Start
to follow naming convention as @jcouv mentioned earlier in the previous PR: Start
and End
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I am not thrilled with Start. It sounds like an action is about to start. That's why I originally did not use End. But I will think about the naming a bit more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @YohDeadfall as Start
seems to fit with End
. However, I also see @KrzysztofCwalina point. I like First
and Last
better, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Begin
would be fine, but unfortunately it shouldn't be used for historical reasons since everywhere in .NET only Start
/End
is used for such situations in parameter names, fields, properties. Also Begin
and End
are used for pre-Task
async operations in method names.
|
||
public bool Contains(int value) | ||
{ | ||
if (!IsBound) throw new InvalidOperationException("Unbound ranges cannot contain"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's unbound, then it contains any value. If it has unbound start, check the end only. Same for ranges with unbound start.
if (IsBound) return Start <= value && value < End;
if (Start == UnboundStart) return value < End;
if (End == UnboundEnd) return Start <= value;
return false;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not forget to check direction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Theoretically yes, but what are the scenarios for calling Contains on unbound ranges?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exceptions are okay for GetEnumerator
since we can't iterate over infinity sequences, but Contains
can work and therefore should. Otherwise, it wouldn't be useful, and developers would be managed to write they own implementation. For example:
class Example
{
private Range _range;
public Example(Range range) => _range = range;
public void DoSomething(int value)
{
if (_range.Contains(value))
{
}
else
{
}
}
}
What if range
provided to the constructor is unbound? Should I use (_range.Start == Range.UnboundStart || _range.Start <= value) && (_range.End == Range.UnboundEnd || value < _range.End)
to know if the value
is in _range
to be sure that the code will not throw?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also the in
pattern matching will use Contains
.
switch (variable)
{
case in ..0: // should not throw
break;
// ...
case in 5..: // should not throw
break;
}
7c45655
to
7a01e8e
Compare
} | ||
|
||
public bool IsBound => First != UnboundedFirst && Last != UnboundedLast; | ||
public bool IsBound => First != UnboundedFirst && End != UnboundedEnd; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IsBound
should take into account that the range could be reversed:
Range(UnboundedLast, UnboundedFirst)
is unbound;Range(5, UnboundedFirst)
is unbound;Range(UnboundedLast, 5)
is unbound too.
I added some helpers and overrode ToString to make it easier to work with ranges.
Renamed Last to End as "Last" is confusing given the value is exclusive.
@ahsonkhan, @joshfree, @eerhardt, @jcouv