Enhanced C#
Language of your choice: library documentation
Public Member Functions | List of all members
Loyc.Collections.IListSource< out out T > Interface Template Reference

A read-only list indexed by an integer. More...

Source file:
Inheritance diagram for Loyc.Collections.IListSource< out out T >:


A read-only list indexed by an integer.

Member list:

public IEnumerator<T> GetEnumerator(); // inherited
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator();
public T this[int index] { get; } // inherited
public int Count { get; } // inherited
public T TryGet(int index, out bool fail);
IRange<T> Slice(int start, int count = int.MaxValue);

The term "source" means a read-only collection, as opposed to a "sink" which is a write-only collection. The purpose of IListSource is to support slices, eliminate the need to call Count before reading from the list, and to make it easier to implement a read-only list, by lifting IList's requirement to write implementations for Add(), Remove(), etc. A secondary purpose is, of course, to guarantee users don't mistakenly call those methods on a read-only collection.

I have often wanted to access the "next" or "previous" item in a list, e.g. during parsing, but it inconvenient if you have to worry about whether the the current item is the first or last. In that case you must check whether the array index is valid, which is both inconvenient and wasteful, because the list class itself will check the array index a second time, and then the .NET runtime will check the index a third time when reading the internal array. The TryGet(index, defaultValue) extension method can be used to return a default value if the index is not valid, using only one interface call.

Design footnote: I would have preferred to define TryGet with one of these signatures:

bool TryGet(int index, out T value);
T? TryGet(int index);
Maybe<T> TryGet(int index);

However, these signatures don't allow T to be covariant (out T). The first signature doesn't work because the .NET runtime doesn't distinguish between ref and out, so T could not be covariant. The second signature doesn't work if T is not a value type. The third signature doesn't work because, as a struct, Maybe<T> is not allowed to be covariant (even though the JIT could, theoretically, treat it covariantly).

Using Impl.ListSourceBase<T> as your base class can help you implement this interface more quickly.

Public Member Functions

TryGet (int index, out bool fail)
 Gets the item at the specified index, and does not throw an exception on failure. More...
IRange< T > Slice (int start, int count=int.MaxValue)
 Returns a sub-range of this list. More...

Member Function Documentation

IRange<T> Loyc.Collections.IListSource< out out T >.Slice ( int  start,
int  count = int.MaxValue 

Returns a sub-range of this list.

startThe new range will start at this index in the current list (this location will be index [0] in the new range).
countThe desired number of elements in the new range, or int.MaxValue to get all elements until the end of the list.
Returns a sub-range of this range.
ArgumentExceptionThe start index was below zero.

The (start, count) range is allowed to be invalid, as long as start is zero or above.

  • If count is below zero, or if start is above the original Count, the Count of the new slice is set to zero.
  • if (start + count) is above the original Count, the Count of the new slice is reduced to this.Count - start. Implementation note: do not compute (start + count) because it may overflow. Instead, test whether (count > this.Count - start).

Most collections should use the following implementation:

IRange<T> IListSource<T>.Slice(int start, int count) { return Slice(start, count); }
public Slice_<T> Slice(int start, int count) { return new Slice_<T>(this, start, count); }

Referenced by Loyc.Collections.DList< Pair< LNode, int > >.Add(), Loyc.Collections.Impl.InternalDList< Loyc.Collections.Impl.SparseAListLeaf.Entry >.Add(), Loyc.Collections.ArraySlice< T >.ArraySlice(), Loyc.Syntax.Lexing.Token.Equals(), Loyc.Collections.ListSlice< LogMessage >.ListSlice(), Loyc.Collections.Impl.InternalList< Loyc.Collections.Impl.IAListTreeObserver< K, T > >.Move(), Loyc.Collections.NegListSlice< T >.NegListSlice(), Loyc.Collections.ListSlice< LogMessage >.RemoveAll(), Loyc.Collections.NegListSource< T >.Slice(), Loyc.Collections.Slice_< T >.Slice_(), and Loyc.Collections.AListBase< K, T >.SwapHelper().

T Loyc.Collections.IListSource< out out T >.TryGet ( int  index,
out bool  fail 

Gets the item at the specified index, and does not throw an exception on failure.

indexAn index in the range 0 to Count-1.
failA flag that is set on failure.
The element at the specified index, or default(T) if the index is not valid.

In my original design, the caller could provide a value to return on failure, but this would not allow T to be marked as "out" in C# 4. For the same reason, we cannot have a ref/out T parameter. Instead, the following extension methods are provided:

bool TryGet(int index, ref T value);
T TryGet(int, T defaultValue);

Referenced by Loyc.Syntax.Les.Les2LanguageService.Parse(), Loyc.Syntax.Les.Les3LanguageService.Parse(), Loyc.Ecs.EcsLanguageService.Parse(), Loyc.Syntax.ParsingService.ParseSingle(), and Loyc.Collections.NegListSource< T >.TryGet().