New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement all companion APIs for System.Index and System.Range #28197
Comments
@terrajobst, is that the full list? I'm wondering if we're planning to, for example, add such Index-based indexers to collections like |
It's the full list of what I had planned. But you're right. We should probably add more, such as:
It might be worthwhile to discuss this. |
Any goals around perf? Methods using Haven't looked at |
For all of these additional types other than |
Please update the specs if you think we need to add more types to the list. I'll start working on that soon. |
Something like this? public static class RangeExtensions
{
public static RangeSlice<TList, T> Slice<TList, T>(this TList list, Range range)
where TList : IList<T>
{
int start = range.Start.FromEnd ? list.Count - range.Start.Value : range.Start.Value;
int end = range.End.FromEnd ? list.Count - range.End.Value : range.End.Value;
return new RangeSlice<TList, T>(list, start, end);
}
public struct RangeSlice<TList, T>
where TList : IList<T>
{
private readonly TList _list;
private readonly int _start;
private readonly int _end;
internal RangeSlice(TList list, int start, int end)
{
if (list == null)
{
throw new ArgumentNullException(nameof(list));
}
if (start < 0 || start >= list.Count)
{
throw new IndexOutOfRangeException(nameof(start));
}
if (end < start || end >= list.Count)
{
throw new IndexOutOfRangeException(nameof(end));
}
_list = list;
_start = start;
_end = end;
}
public T this[int index]
{
get => _list[GetIndex(index)];
set => _list[GetIndex(index)] = value;
}
private int GetIndex(int index)
{
checked
{
int newIndex = index + _start;
if (newIndex >= _end)
{
throw new IndexOutOfRangeException();
}
return newIndex;
}
}
public int Count => _start - _end;
public Enumerator GetEnumerator() => new Enumerator(_list, _start, _end);
public struct Enumerator : IEnumerator<T>
{
private readonly TList _list;
private readonly int _start;
private readonly int _end;
private int _index;
internal Enumerator(TList list, int start, int end)
{
_list = list;
_start = start;
_end = end;
_index = start - 1;
Current = default;
}
public T Current { get; private set; }
public bool MoveNext()
{
checked
{
_index++;
}
if (_index < _end)
{
Current = _list[_index];
return true;
}
Current = default;
return false;
}
public void Dispose(){}
object IEnumerator.Current => Current;
void IEnumerator.Reset() => _index = _start - 1;
}
}
} Though perf would be worse than C# just translating it, especially since it will likely be interface dispatch (shared generic) rather than direct calls (specialised generic). |
Would be nice if there was a faster way of capturing this in the type system via C# without rewriting it e.g. converting the nice foreach (var item in list[range])
{
// do something with item
} To the less nice, but concretely typed int start = range.Start.FromEnd ? list.Count - range.Start.Value : range.Start.Value;
int end = range.End.FromEnd ? list.Count - range.End.Value : range.End.Value;
for (var i = start; i < end; i++)
{
var item = list[i];
// do something with item
} In csharplang the community would tell me shapes would solve the perf 😉 |
I feel like My suggestion would be to adjust the names to be clearer, though it would switch around which does what: |
This work was discussed and completed by @tarekgh. |
The spec is here.
Preview 1 Shipped Version
Open Issues
GetLength(int containerLength)
orRange Close(int containerLength)
^0
in the range is pointing at beyond last element of the container.The text was updated successfully, but these errors were encountered: