Класс наследуется со списком и исключением при попытке прочитать его - PullRequest
0 голосов
/ 08 декабря 2011

Оригинальный вопрос

У меня такая ситуация:

type
  X1 = Class
  protected
    oMyList: TMyList; 
  public
    property MyList: TMyList write oMyList;
  end;

  X2 = Class(X1)
  public
    procedure GetMyList;
  end;

с:

  procedure X2.GetMyList;
  begin
    Writeln (oMyList.Count);    // <-- Return exception
  end;

и в основной программе:

var
  P: X2;
  MyList: TMyList;
begin
  P := X2.Create;
  try
    P.MyList := MyList;
    P.GetMyList;
  finally
    P.Free;
  end;
end;

Проблема является исключением, когда я пытаюсь прочитать oMyList.Count. Конечно, MyList создан и определен правильно.

Где моя ошибка?

Обновлен вопрос

У меня такая ситуация:

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, System.Generics.Collections;

// Module 1

type
  PDBEstr = Integer;  // Just an example of list with integer
  TDBEstr = TList<PDBEstr>;

  TArchive = class
  protected
    oDBEstr: TDBEstr;
  public
    constructor Create;
    destructor Free;
    property DBEstr: TDBEstr read oDBEstr;
  end;

constructor TArchive.Create;
begin
  oDBEstr := TList<PDBEstr>.Create;
  oDBEstr.Add(36);  // Add an element to list
end;

destructor TArchive.Free;
begin
  oDBEstr.Free;
end;

// Module 2

type
  TX0 = class
  protected
    oArchive: TArchive;
    function GetDBEstr: TDBEstr;
  public
    constructor Create;
    destructor Free;
    property DBEstr: TDBEstr read GetDBEstr;
  end;

constructor TX0.Create;
begin
  oArchive := TArchive.Create;
end;

destructor TX0.Free;
begin
  oArchive.Free;
end;

function TX0.GetDBEstr: TDBEstr;
begin
  Result := oArchive.DBEstr;
end;

// Module 3

type
  TX1 = class
  var
    oDBEstr: TDBEstr;
  public
    property DBEstr: TDBEstr read oDBEstr write oDBEstr;
    procedure Load;
  end;

procedure TX1.Load;
begin
  writeln (oDBEstr.Count);  // Return 1
end;

// Module 4

type
  TX2 = class(TX1)
  public
    constructor Create;
  end;

constructor TX2.Create;
begin
  inherited;
  // In this point i need to have access to oDBEstr for work with data
  // in oDBEstr
  writeln(oDBEstr.Count);   // <----- Return Exception
end;

// Main Program

var
  X0: TX0;
  X2: TX2;
begin
  try
    X0 := TX0.Create;
    try
      writeln(X0.DBEstr.Count);  // Return 1
      writeln(X0.DBEstr.First);  // Return 36
      X2 := TX2.Create;
      try
        X2.DBEstr := X0.DBEstr;
        writeln(X2.DBEstr.count);  // Return 1
      finally
        X2.Free;
      end;
    finally
      X0.Free;
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

Все работает отлично, только в TX2.Create У меня есть исключение, когда я пытаюсь прочитать oDBEstr. Конечно, я беру в качестве примера TX2.Create, но мне нужно иметь доступ к oDBEstr во всех TX2, а не только в TX2.Create.

Ответы [ 2 ]

3 голосов
/ 08 декабря 2011

Примечание: Этот ответ относится к исходному вопросу.

Локальная переменная MyList не была создана в разделе кода, который вы пометили основная программа .Соответственно, ссылка на объект будет иметь некоторое значение мусора из стека.При первом входе в список происходит нарушение прав доступа.

0 голосов
/ 10 декабря 2011

Примечание: Этот ответ относится к обновленному вопросу.

В своем обновленном коде вы просто не создаете oDBEstr, прежде чем получить к нему доступ.Конструктор TX2.Create не создает его, а класс, из которого получен TX2, не имеет конструктора.Таким образом, естественно происходит нарушение доступа.

Намерение этих классов, по-видимому, состоит в том, что TX1 и, следовательно, TX2 содержит ссылку на DBEstr, которая принадлежит другим классом.Вы реализовали свойство чтения / записи, которое позволяет пользователям TX1 и TX2 устанавливать эту ссылку.Тем не менее, они не могут сделать это, пока конструктор не закончил работу.Пользователи TX1 и TX2 могут установить это свойство, только если у них есть ссылка на TX1 или TX2.Таким образом, для решения загадки ваш единственный вариант - передать экземпляр DBEstr конструктору TXE2 в качестве параметра.

Вот рабочая версия вашего кода, которая иллюстрирует, как это сделать:

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, System.Generics.Collections;

// Module 1

type
  PDBEstr = Integer;  // Just an example of list with integer
  TDBEstr = TList<PDBEstr>;

  TArchive = class
  protected
    oDBEstr: TDBEstr;
  public
    constructor Create;
    destructor Destroy; override;
    property DBEstr: TDBEstr read oDBEstr;
  end;

constructor TArchive.Create;
begin
  oDBEstr := TList<PDBEstr>.Create;
  oDBEstr.Add(36);  // Add an element to list
end;

destructor TArchive.Destroy;
begin
  oDBEstr.Free;
  inherited;
end;

// Module 2

type
  TX0 = class
  protected
    oArchive: TArchive;
    function GetDBEstr: TDBEstr;
  public
    constructor Create;
    destructor Destroy; override;
    property DBEstr: TDBEstr read GetDBEstr;
  end;

constructor TX0.Create;
begin
  oArchive := TArchive.Create;
end;

destructor TX0.Destroy;
begin
  oArchive.Free;
  inherited;
end;

function TX0.GetDBEstr: TDBEstr;
begin
  Result := oArchive.DBEstr;
end;

// Module 3

type
  TX1 = class
  var
    oDBEstr: TDBEstr;
  public
    property DBEstr: TDBEstr read oDBEstr write oDBEstr;
    procedure Load;
  end;

procedure TX1.Load;
begin
  writeln (oDBEstr.Count);  // Return 1
end;

// Module 4

type
  TX2 = class(TX1)
  public
    constructor Create(ADBEstr: TDBEstr);
  end;

constructor TX2.Create(ADBEstr: TDBEstr);
begin
  inherited Create;
  oDBEstr := ADBEStr;
  writeln(oDBEstr.Count);
end;

// Main Program

var
  X0: TX0;
  X2: TX2;
begin
  try
    X0 := TX0.Create;
    try
      writeln(X0.DBEstr.Count);  // Return 1
      writeln(X0.DBEstr.First);  // Return 36
      X2 := TX2.Create(X0.DBEstr);
      try
        writeln(X2.DBEstr.count);  // Return 1
      finally
        X2.Free;
      end;
    finally
      X0.Free;
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

Я понимаю, что этот код является иллюстративным примером, взятым из вашего реального кода, но я сомневаюсь в использованиисвойство чтения / записи для TX1.DBEstr.Я подозреваю, что каждый экземпляр TX1 будет использовать один и тот же экземпляр DBEstr в течение всего времени существования экземпляра TX1.Если это так, то он должен быть передан конструктору в TX1, и свойство будет доступно только для чтения:

type
  TX1 = class
  var
    oDBEstr: TDBEstr;
  public
    constructor Create(ADBEstr: TDBEstr);
    property DBEstr: TDBEstr read oDBEstr;
    procedure Load;
  end;

constructor TX1.Create(ADBEstr: TDBEstr);
begin
  inherited Create;
  oDBEstr := ADBEStr;
end;

Это изменение приведет к следующей реализации для TX2

type
  TX2 = class(TX1)
  public
    constructor Create(ADBEstr: TDBEstr);
  end;

constructor TX2.Create(ADBEstr: TDBEstr);
begin
  inherited;
  writeln(oDBEstr.Count);
end;

Обратите внимание, что я исправил деструкторы в вашем коде.Деструкторы всегда должны называться Destroy, они всегда должны быть объявлены с ключевым словом override, и они всегда должны вызывать inherited в качестве последнего действия.Обратите внимание, что вы никогда не звоните Destroy напрямую, а звоните Free.Это соглашение должно разрешить Free быть нулевой операцией для неинициализированной ссылки на объект nil, что очень удобно при кодировании деструкторов.

...