Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.143.9.115] |
|
Сообщ.
#1
,
|
|
|
Здрасте! Я все туплю
Пробую прикрутить енумератор к классу TPerson, но ошибка: [dcc32 Error]: E2291 Missing implementation of interface method IEnumerator.GetCurrent Имеем следующую реализацию, что ту не так? Вроде метод GetCurrent реализован, но компиль не видит его program PersonEnumeratorDemo; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils; type TPerson = class Name: string; Age: integer; constructor Create(AName: string; AAge: integer); end; TPersonList = class public FPersons: TArray<TPerson>; public function GetEnumerator: IEnumerator<TPerson>; procedure Add(APerson: TPerson); end; TPersonEnumerator = class(TInterfacedObject, IEnumerator<TPerson>) private FPersons: TArray<TPerson>; FIndex: integer; function GetCurrent: TPerson; public constructor Create(APersons: TArray<TPerson>); function MoveNext: Boolean; procedure Reset; property Current: TPerson read GetCurrent; end; { TPerson } constructor TPerson.Create(AName: string; AAge: integer); begin Name:= AName; Age:= AAge; end; { TPersonList } procedure TPersonList.Add(APerson: TPerson); begin FPersons:= FPersons + [APerson]; end; function TPersonList.GetEnumerator: IEnumerator<TPerson>; begin Result:= TPersonEnumerator.Create(FPersons); end; { TPersonEnumerator } constructor TPersonEnumerator.Create(APersons: TArray<TPerson>); begin FPersons:= APersons; FIndex:= -1; end; function TPersonEnumerator.GetCurrent: TPerson; begin Result:= FPersons[FIndex]; end; function TPersonEnumerator.MoveNext: Boolean; begin Result:= FIndex < High(FPersons); if Result then Inc(FIndex); end; procedure TPersonEnumerator.Reset; begin FIndex:= -1; end; begin end. |
Сообщ.
#2
,
|
|
|
требуется реализация
function GetCurrent: TObject; из IEnumerator, а не IEnumerator<TPerson> что, очевидно, в одном классе сделать нельзя. требуется промежуточный (TBasePersonEnumerator - TInterfacedObject и IEnumerator) и конечный (TPersonEnumerator - TBasePersonEnumerator и IEnumerator<TPerson>) а почему именно интерфейс? uses generics.collections и унаследуйся от TEnumerator<TPerson> |
Сообщ.
#3
,
|
|
|
Энумератор достаточно сделать записью.
|
Сообщ.
#4
,
|
|
|
Цитата Shaggy @ требуется реализация function GetCurrent: TObject; из IEnumerator, а не IEnumerator<TPerson> что, очевидно, в одном классе сделать нельзя. требуется промежуточный (TBasePersonEnumerator - TInterfacedObject и IEnumerator) и конечный (TPersonEnumerator - TBasePersonEnumerator и IEnumerator<TPerson>) а почему именно интерфейс? uses generics.collections и унаследуйся от TEnumerator<TPerson> Вау! Пашет! Спасибо! Протестил и так и так uses System.SysUtils; type .... TBasePersonEnumerator = class (TInterfacedObject, IEnumerator) private FPersons: TArray<TPerson>; FIndex: integer; function GetCurrent: TObject; public constructor Create(APersons: TArray<TPerson>); function MoveNext: Boolean; procedure Reset; end; TPersonEnumerator = class(TBasePersonEnumerator, IEnumerator<TPerson>) private function GetCurrent: TPerson; public constructor Create(APersons: TArray<TPerson>); property Current: TPerson read GetCurrent; end; .... { TBasePersonEnumerator } constructor TBasePersonEnumerator.Create(APersons: TArray<TPerson>); begin FPersons:= APersons; FIndex:= -1; end; function TBasePersonEnumerator.GetCurrent: TObject; begin Result:= FPersons[FIndex]; end; function TBasePersonEnumerator.MoveNext: Boolean; begin Result:= FIndex < High(FPersons); if Result then Inc(FIndex); end; procedure TBasePersonEnumerator.Reset; begin FIndex:= -1; end; { TPersonEnumerator } constructor TPersonEnumerator.Create(APersons: TArray<TPerson>); begin inherited Create(APersons); end; function TPersonEnumerator.GetCurrent: TPerson; begin Result:= TPerson(inherited GetCurrent); end; // test begin var persons:= TPersonList.Create; try persons.Add(TPerson.Create('Gregory', 42)); persons.Add(TPerson.Create('Mark', 27)); persons.Add(TPerson.Create('Julia', 32)); for var p in persons do begin Writeln(Format('%s is %d years old.', [p.Name, p.Age])); end; finally persons.Free; end; end. Добавлено Цитата Fr0sT @ Энумератор достаточно сделать записью. Это как? Или имеется ввиду просто без реализации IEnumerator? Так это я знаю, мене интересовала реализаци IEnumerator<T> |
Сообщ.
#5
,
|
|
|
А зачем IEnumerator вообще нужен?
|
Сообщ.
#6
,
|
|
|
Цитата Fr0sT @ А зачем IEnumerator вообще нужен? Я без понятия почитайте книжку дяди Коли 'Фигачим на Делфине ' |
Сообщ.
#7
,
|
|
|
Цитата Shaggy @ требуется реализация function GetCurrent: TObject; из IEnumerator, а не IEnumerator<TPerson> что, очевидно, в одном классе сделать нельзя. Можно! http://docwiki.embarcadero.com/RADStudio/Rio/en/Implementing_Interfaces см. раздел Method Resolution Clause. TPersonEnumerator = class(TInterfacedObject, IEnumerator<TPerson>) private FPersons: TArray<TPerson>; FIndex: integer; function GetCurrent: TObject; //<-- 1 function GetCurrentPerson: TPerson; //<-- 2 function IEnumerator<TPerson>.GetCurrent = GetCurrentPerson; //<-- 3 public constructor Create(APersons: TArray<TPerson>); function MoveNext: Boolean; procedure Reset; property Current: TPerson read GetCurrentPerson; end; { TPersonEnumerator } constructor TPersonEnumerator.Create(APersons: TArray<TPerson>); begin FPersons:= APersons; FIndex:= -1; end; function TPersonEnumerator.GetCurrent: TObject; begin Result:= FPersons[FIndex]; end; function TPersonEnumerator.GetCurrentPerson: TPerson; begin Result:= TPerson(GetCurrent); end; function TPersonEnumerator.MoveNext: Boolean; begin Result:= FIndex < High(FPersons); if Result then Inc(FIndex); end; procedure TPersonEnumerator.Reset; begin FIndex := -1; end; |
Сообщ.
#8
,
|
|
|
Цитата Fr0sT @ А зачем IEnumerator вообще нужен? Потому что эмбаркадеро бездумно скопировало эту иерархию с .NET без оглядки на отличия дотнетовской и дельфийской системы типов. В .NET любой тип (в том числе числа, структуры (аналог дельфийского record) - является наследником object. А значит перечисление(Enumerable) любого типа можно представить как перечисление объектов. Это может быть полезно для неких обобщенных алгоритмов, ну например: void WriteItemsToConsole(IEnumerable items) { foreach(var item in items) Console.WriteLine(item.ToString()); } WriteItemsToConsole(new double[] {1.1,2.1,3.14}); Но в дельфе список тех же Double не возможно представить в виде объектов. Так что иерархия System.IEnumerable - сломана. В том же spring4d своя иерархия IEnumerable и НЕдженерик IEnumerable - это список Rtti.TValue, там Стефан нормально сделал, любой тип может быть представлен как TValue |
Сообщ.
#9
,
|
|
|
Пробую заюзать Spring4D в моем примере, и вроде бы все верно но ошибка доступа по нолевому адресу
Немного дебагнул выяснил что итерация идет даже когда список исчерпан uses System.SysUtils , System.RTTI , Spring.Collections , Spring.Collections.Lists ; type TPerson = class Name: string; Age: integer; constructor Create(AName: string; AAge: integer); end; TPersonList = class(TObjectList<TPerson>) public function GetEnumerator: IEnumerator<TPerson>; override; end; TPersonEnumerator = class(TInterfacedObject, IEnumerator<TPerson>) private FPersons: TArray<TPerson>; FIndex: integer; function GetCurrent: TValue; function GetCurrentPerson: TPerson; function IEnumerator<TPerson>.GetCurrent = GetCurrentPerson; public constructor Create(APersons: TArray<TPerson>); function MoveNext: Boolean; procedure Reset; property Current: TPerson read GetCurrentPerson; end; { TPerson } constructor TPerson.Create(AName: string; AAge: integer); begin Name:= AName; Age:= AAge; end; { TPersonList } function TPersonList.GetEnumerator: IEnumerator<TPerson>; begin Result:= TPersonEnumerator.Create(GetItems); end; { TPersonEnumerator } constructor TPersonEnumerator.Create(APersons: TArray<TPerson>); begin FPersons:= APersons; FIndex:= -1; end; function TPersonEnumerator.GetCurrent: TValue; begin Result:= FPersons[FIndex]; end; function TPersonEnumerator.GetCurrentPerson: TPerson; begin Result:= GetCurrent.AsType<TPerson>; end; function TPersonEnumerator.MoveNext: Boolean; begin Result:= FIndex < High(FPersons); if Result then Inc(FIndex); end; procedure TPersonEnumerator.Reset; begin FIndex := -1; end; // test begin var persons:= TPersonList.Create([ TPerson.Create('Gregory', 42), TPerson.Create('Mark', 27) ]); persons.Add(TPerson.Create('Julia', 32)); var names := TEnumerable.Select<TPerson, string>(persons, function (p: TPerson): string begin Result:= p.Name; //<-- тут AV доступ по нолевому p end); names.ForEach(procedure (const name: string) begin Writeln(Format('%s', [name])); end); end; end. Проверил число персон в массиве оказалось 4! Откуда хз Применил хак в MoveNext енумератора минусовал 1 от High(FPersons) и заработалло, но вопрос остается откуда там 4 морды когда я добавил тока 3 Проверил на дефолтном енумераторе TObjectList, закомметив свой енум пашет без AV Кажестся понял в TObjectList юзается какой то TArrayManager и вроде бы он добавил еще один итем, ну типо прозапас для эфективности вооот... я ж получаю получается этот сырой массив! Вопрос может я ваще не должен был оверайдить GetEnumerator? Решил т.о. передал в конструктор енумератора еще и Count и в MoveNext чекал FIndex < FCount - 1 |
Сообщ.
#10
,
|
|
|
Да, в List массив создается с запасом
|