На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! ПРАВИЛА РАЗДЕЛА · FAQ раздела Delphi · Книги по Delphi
Пожалуйста, выделяйте текст программы тегом [сode=pas] ... [/сode]. Для этого используйте кнопку [code=pas] в форме ответа или комбобокс, если нужно вставить код на языке, отличном от Дельфи/Паскаля.
Следующие вопросы задаются очень часто, подробно разобраны в FAQ и, поэтому, будут безжалостно удаляться:
1. Преобразовать переменную типа String в тип PChar (PAnsiChar)
2. Как "свернуть" программу в трей.
3. Как "скрыться" от Ctrl + Alt + Del (заблокировать их и т.п.)
4. Как прочитать список файлов, поддиректорий в директории?
5. Как запустить программу/файл?
... (продолжение следует) ...

Вопросы, подробно описанные во встроенной справочной системе Delphi, не несут полезной тематической нагрузки, поэтому будут удаляться.
Запрещается создавать темы с просьбой выполнить какую-то работу за автора темы. Форум является средством общения и общего поиска решения. Вашу работу за Вас никто выполнять не будет.


Внимание
Попытки открытия обсуждений реализации вредоносного ПО, включая различные интерпретации спам-ботов, наказывается предупреждением на 30 дней.
Повторная попытка - 60 дней. Последующие попытки бан.
Мат в разделе - бан на три месяца...
Модераторы: jack128, D[u]fa, Shaggy, Rouse_
  
> IEnumerator<T> , [dcc32 Error]: E2291 Missing implementation of interface method IEnumerator.GetCurrent
    Здрасте! Я все туплю :D
    Пробую прикрутить енумератор к классу TPerson, но ошибка:
    [dcc32 Error]: E2291 Missing implementation of interface method IEnumerator.GetCurrent
    Имеем следующую реализацию, что ту не так?
    Вроде метод GetCurrent реализован, но компиль не видит его :wacko:
    ExpandedWrap disabled
      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.
    Сообщение отредактировано: Cfon -
      требуется реализация
      function GetCurrent: TObject;
      из IEnumerator, а не IEnumerator<TPerson>
      что, очевидно, в одном классе сделать нельзя.
      требуется промежуточный (TBasePersonEnumerator - TInterfacedObject и IEnumerator)
      и конечный (TPersonEnumerator - TBasePersonEnumerator и IEnumerator<TPerson>)

      а почему именно интерфейс?
      uses generics.collections
      и унаследуйся от TEnumerator<TPerson>
        Энумератор достаточно сделать записью.
          Цитата Shaggy @
          требуется реализация
          function GetCurrent: TObject;
          из IEnumerator, а не IEnumerator<TPerson>
          что, очевидно, в одном классе сделать нельзя.
          требуется промежуточный (TBasePersonEnumerator - TInterfacedObject и IEnumerator)
          и конечный (TPersonEnumerator - TBasePersonEnumerator и IEnumerator<TPerson>)

          а почему именно интерфейс?
          uses generics.collections
          и унаследуйся от TEnumerator<TPerson>

          Вау! Пашет! Спасибо! :thanks:
          Протестил и так и так :D
          ExpandedWrap disabled
            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 @
          Энумератор достаточно сделать записью.

          Это как? :D
          Или имеется ввиду просто без реализации IEnumerator? Так это я знаю, мене интересовала реализаци IEnumerator<T> :D
          Сообщение отредактировано: Cfon -
            А зачем IEnumerator вообще нужен?
              Цитата Fr0sT @
              А зачем IEnumerator вообще нужен?

              Я без понятия :D
              почитайте книжку дяди Коли 'Фигачим на Делфине ' :lool:
                Цитата Shaggy @
                требуется реализация
                function GetCurrent: TObject;
                из IEnumerator, а не IEnumerator<TPerson>
                что, очевидно, в одном классе сделать нельзя.

                Можно! :D
                http://docwiki.embarcadero.com/RADStudio/Rio/en/Implementing_Interfaces
                см. раздел Method Resolution Clause.
                ExpandedWrap disabled
                  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;
                  Цитата Fr0sT @
                  А зачем IEnumerator вообще нужен?

                  Потому что эмбаркадеро бездумно скопировало эту иерархию с .NET без оглядки на отличия дотнетовской и дельфийской системы типов.
                  В .NET любой тип (в том числе числа, структуры (аналог дельфийского record) - является наследником object. А значит перечисление(Enumerable) любого типа можно представить как перечисление объектов. Это может быть полезно для неких обобщенных алгоритмов, ну например:

                  ExpandedWrap disabled
                    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
                  Сообщение отредактировано: jack128 -
                    Пробую заюзать Spring4D в моем примере, и вроде бы все верно но ошибка доступа по нолевому адресу :huh:
                    Немного дебагнул выяснил что итерация идет даже когда список исчерпан :wacko:
                    ExpandedWrap disabled
                      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! Откуда хз :D
                    Применил хак :D в MoveNext енумератора минусовал 1 от High(FPersons) и заработалло, но вопрос остается откуда там 4 морды когда я добавил тока 3 :-?

                    Проверил на дефолтном енумераторе TObjectList, закомметив свой енум пашет без AV :scratch:
                    Кажестся понял в TObjectList юзается какой то TArrayManager и вроде бы он добавил еще один итем, ну типо прозапас для эфективности вооот... я ж получаю получается этот сырой массив! :D
                    Вопрос может я ваще не должен был оверайдить GetEnumerator?

                    Решил т.о. передал в конструктор енумератора еще и Count и в MoveNext чекал FIndex < FCount - 1 :D
                    Сообщение отредактировано: Cfon -
                      Да, в List массив создается с запасом
                      0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                      0 пользователей:


                      Рейтинг@Mail.ru
                      [ Script execution time: 0,0359 ]   [ 17 queries used ]   [ Generated: 19.04.24, 09:20 GMT ]