unit ReadOnlyList; interface // Eigen gemaakt list gebaseerd op TList zonder methodes om de lijst te manipuleren na creatie. uses System.Generics.Collections, System.Generics.Defaults, System.SysUtils, System.RTLConsts; type TReadOnlyList = class(TEnumerable) private FItems: array of T; FCount: Integer; FComparer: IComparer; FOnNotify: TCollectionNotifyEvent; function GetCapacity: Integer; procedure SetCapacity(Value: Integer); procedure SetCount(Value: Integer); function GetItem(Index: Integer): T; procedure Grow(ACount: Integer); procedure GrowCheck(ACount: Integer); inline; procedure DeleteRange(AIndex, ACount: Integer); procedure Insert(Index: Integer; const Value: T); procedure InsertRange(Index: Integer; const Collection: TEnumerable); overload; protected function DoGetEnumerator: TEnumerator; override; procedure Notify(const Item: T; Action: TCollectionNotification); virtual; public constructor Create; overload; constructor Create(const AComparer: IComparer); overload; constructor Create(Collection: TEnumerable); overload; destructor Destroy; override; function First: T; function Last: T; function Contains(const Value: T): Boolean; function IndexOf(const Value: T): Integer; function LastIndexOf(const Value: T): Integer; function BinarySearch(const Item: T; out Index: Integer): Boolean; overload; function BinarySearch(const Item: T; out Index: Integer; const AComparer: IComparer): Boolean; overload; procedure TrimExcess; function ToArray: TArray; override; final; property Capacity: Integer read GetCapacity write SetCapacity; property Count: Integer read FCount write SetCount; // property Items[Index: Integer]: T read GetItem write SetItem; default; property Items[Index: Integer]: T read GetItem; default; property OnNotify: TCollectionNotifyEvent read FOnNotify write FOnNotify; type TEnumerator = class(TEnumerator) private FList: TReadOnlyList; FIndex: Integer; function GetCurrent: T; protected function DoGetCurrent: T; override; function DoMoveNext: Boolean; override; public constructor Create(AList: TReadOnlyList); property Current: T read GetCurrent; function MoveNext: Boolean; end; function GetEnumerator: TEnumerator; reintroduce; end; implementation function TReadOnlyList.GetCapacity: Integer; begin Result := Length(FItems); end; procedure TReadOnlyList.SetCapacity(Value: Integer); begin if Value < Count then Count := Value; SetLength(FItems, Value); end; procedure TReadOnlyList.SetCount(Value: Integer); begin if Value < 0 then raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); if Value > Capacity then SetCapacity(Value); if Value < Count then DeleteRange(Value, Count - Value); FCount := Value; end; function TReadOnlyList.GetItem(Index: Integer): T; begin if (Index < 0) or (Index >= Count) then raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); Result := FItems[Index]; end; procedure TReadOnlyList.Grow(ACount: Integer); var newCount: Integer; begin newCount := Length(FItems); if newCount = 0 then newCount := ACount else repeat newCount := newCount * 2; if newCount < 0 then OutOfMemoryError; until newCount >= ACount; Capacity := newCount; end; procedure TReadOnlyList.GrowCheck(ACount: Integer); begin if ACount > Length(FItems) then Grow(ACount) else if ACount < 0 then OutOfMemoryError; end; procedure TReadOnlyList.Notify(const Item: T; Action: TCollectionNotification); begin if Assigned(FOnNotify) then FOnNotify(Self, Item, Action); end; constructor TReadOnlyList.Create; begin Create(TComparer.Default); end; constructor TReadOnlyList.Create(const AComparer: IComparer); begin inherited Create; FComparer := AComparer; if FComparer = nil then FComparer := TComparer.Default; end; constructor TReadOnlyList.Create(Collection: TEnumerable); begin inherited Create; FComparer := TComparer.Default; InsertRange(0, Collection); end; destructor TReadOnlyList.Destroy; begin Capacity := 0; inherited; end; function TReadOnlyList.DoGetEnumerator: TEnumerator; begin Result := GetEnumerator; end; function TReadOnlyList.BinarySearch(const Item: T; out Index: Integer): Boolean; begin Result := TArray.BinarySearch(FItems, Item, Index, FComparer, 0, Count); end; function TReadOnlyList.BinarySearch(const Item: T; out Index: Integer; const AComparer: IComparer): Boolean; begin Result := TArray.BinarySearch(FItems, Item, Index, AComparer, 0, Count); end; procedure TReadOnlyList.Insert(Index: Integer; const Value: T); begin if (Index < 0) or (Index > Count) then raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); GrowCheck(Count + 1); if Index <> Count then begin System.Move(FItems[Index], FItems[Index + 1], (Count - Index) * SizeOf(T)); FillChar(FItems[Index], SizeOf(FItems[Index]), 0); end; FItems[Index] := Value; Inc(FCount); Notify(Value, cnAdded); end; procedure TReadOnlyList.InsertRange(Index: Integer; const Collection: TEnumerable); var Item: T; begin for Item in Collection do begin Insert(Index, Item); Inc(Index); end; end; function TReadOnlyList.First: T; begin Result := Items[0]; end; procedure TReadOnlyList.DeleteRange(AIndex, ACount: Integer); var oldItems: array of T; tailCount, i: Integer; begin if (AIndex < 0) or (ACount < 0) or (AIndex + ACount > Count) or (AIndex + ACount < 0) then raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); if ACount = 0 then Exit; SetLength(oldItems, ACount); System.Move(FItems[AIndex], oldItems[0], ACount * SizeOf(T)); tailCount := Count - (AIndex + ACount); if tailCount > 0 then begin System.Move(FItems[AIndex + ACount], FItems[AIndex], tailCount * SizeOf(T)); FillChar(FItems[Count - ACount], ACount * SizeOf(T), 0); end else begin FillChar(FItems[AIndex], ACount * SizeOf(T), 0); end; Dec(FCount, ACount); for i := 0 to Length(oldItems) - 1 do Notify(oldItems[i], cnRemoved); end; function TReadOnlyList.Contains(const Value: T): Boolean; begin Result := IndexOf(Value) >= 0; end; function TReadOnlyList.IndexOf(const Value: T): Integer; var i: Integer; begin for i := 0 to Count - 1 do if FComparer.Compare(FItems[i], Value) = 0 then Exit(i); Result := -1; end; function TReadOnlyList.Last: T; begin Result := Items[Count - 1]; end; function TReadOnlyList.LastIndexOf(const Value: T): Integer; var i: Integer; begin for i := Count - 1 downto 0 do if FComparer.Compare(FItems[i], Value) = 0 then Exit(i); Result := -1; end; function TReadOnlyList.ToArray: TArray; var i: Integer; begin SetLength(Result, Count); for i := 0 to Count - 1 do Result[i] := Items[i]; end; procedure TReadOnlyList.TrimExcess; begin Capacity := Count; end; function TReadOnlyList.GetEnumerator: TEnumerator; begin Result := TEnumerator.Create(Self); end; { TReadOnlyList.TEnumerator } constructor TReadOnlyList.TEnumerator.Create(AList: TReadOnlyList); begin inherited Create; FList := AList; FIndex := -1; end; function TReadOnlyList.TEnumerator.DoGetCurrent: T; begin Result := GetCurrent; end; function TReadOnlyList.TEnumerator.DoMoveNext: Boolean; begin Result := MoveNext; end; function TReadOnlyList.TEnumerator.GetCurrent: T; begin Result := FList[FIndex]; end; function TReadOnlyList.TEnumerator.MoveNext: Boolean; begin if FIndex >= FList.Count then Exit(False); Inc(FIndex); Result := FIndex < FList.Count; end; end.