powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Создание TEncoding в классовом конструкторе
19 сообщений из 19, страница 1 из 1
Создание TEncoding в классовом конструкторе
    #39968549
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Есть такой класс
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
type
  TTest1 = class
  strict private
    class var
      FEncoding: TEncoding;
      FObj: TObject;
  strict private
    class constructor Create;
    class destructor Destroy;
  public
    class property Encoding: TEncoding read FEncoding;
    class property Obj: TObject read FObj;
  end;

class constructor TTest1.Create;
begin
  FEncoding := TEncoding.GetEncoding(1251);
  FObj := TObject.Create;
end;

class destructor TTest1.Destroy;
begin
  FObj.Free;
  FEncoding.Free;
end;

если сделать такой вызов
Код: pascal
1.
2.
3.
4.
5.
6.
7.
procedure TForm1.Button1Click(Sender: TObject);
begin
  if TTest1.Obj <> nil then
    ShowMessage('OK')
  else
    ShowMessage('Fail');
end;

то увидим OK. А если такой
Код: pascal
1.
2.
3.
4.
5.
6.
7.
procedure TForm1.Button1Click(Sender: TObject);
begin
  if TTest1.>>>Encoding<< <> nil then
    ShowMessage('OK')
  else
    ShowMessage('Fail');
end;

получим Fail. Т.е. для второго вызова классовый конструктор не вызывается.

Кто объяснит причину бага?

Повторяется на XE3 и 10.3.1
С уважением, Vasilisk
...
Рейтинг: 0 / 0
Создание TEncoding в классовом конструкторе
    #39968557
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
С таким объявлением
Код: pascal
1.
class property Encoding: TEncoding read GetEncoding;

работает
...
Рейтинг: 0 / 0
Создание TEncoding в классовом конструкторе
    #39968562
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Баг завязан на конкретный класс. Если заменить TEncoding на TMBCSEncoding то все работает
...
Рейтинг: 0 / 0
Создание TEncoding в классовом конструкторе
    #39968566
Kazantsev Alexey
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
_Vasilisk_,

Баг компилятора. На 10.4 воспроизводится. Можно репортить.
...
Рейтинг: 0 / 0
Создание TEncoding в классовом конструкторе
    #39968569
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Обошел багу так
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
type
  TTest = class
  strict private
    type
      TBugFixEncoding = class(TEncoding);
    class var
      FEncoding: TBugFixEncoding;
  strict private
    class constructor Create;
    class destructor Destroy;
  public
    class property Encoding: TBugFixEncoding read FEncoding;
  end;

class constructor TTest.Create;
begin
  FEncoding := TBugFixEncoding(TEncoding.GetEncoding(1251));
end;

class destructor TTest.Destroy;
begin
  FEncoding.Free;
end;
...
Рейтинг: 0 / 0
Создание TEncoding в классовом конструкторе
    #39968601
shalamyansky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Порядок вызовов конструкторов классов неопределен. Все классы (наверное) создаются при старте приложения, но очередность создания непонятна. Вполне возможно, что в момент вызова
Код: pascal
1.
2.
3.
4.
5.
class constructor TTest1.Create;
begin
  FEncoding := TEncoding.GetEncoding(1251);
  FObj := TObject.Create;
end;


класс TEncoding или класс TMBCSEncoding, к которому идет обращение внутри, еще не существует, и экземпляр TMBCSEncoding не может быть создан. Логично ожидать Access Violation в таком случае, однако, вероятно, там предусмотрены пути обхода.

Когда же вы вызываете GetEncoding как геттер при действиях на форме, к этому моменту приложение уже живет полной жизнью и все классы давно созданы.

Ну, может, не совсем так, но засада где-то рядом. Еще не созданы все классы, а вы уже хотите создать экземпляр.
...
Рейтинг: 0 / 0
Создание TEncoding в классовом конструкторе
    #39968606
shalamyansky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Если я прав, то ваш обход работает лишь до поры, до времени. В один прекрасный момент компилятор решит изменить порядок вызова конструкторов, и вы опять напоретесь на ту же ошибку.
...
Рейтинг: 0 / 0
Создание TEncoding в классовом конструкторе
    #39968618
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
shalamyansky
класс TEncoding или класс TMBCSEncoding, к которому идет обращение внутри, еще не существует
Но если объявить FEncoding: TMBCSEncoding или свойство возвращать не доступом к полю, а геттером, то все работает
shalamyansky
Логично ожидать Access Violation в таком случае, однако, вероятно, там предусмотрены пути обхода.
Это не пути обхода. Это баг. При циклической ссылки юнитов мы получаем ошибку компиляции, а не попытки компилятора это пропустить. Построить дерево зависимостей для выполнении initialize секции модулей у компилятора проблем не вызывает
shalamyansky
Когда же вы вызываете GetEncoding как геттер при действиях на форме, к этому моменту приложение уже живет полной жизнью и все классы давно созданы.
Одна проблема - классовый конструктор вызывается в момент старта приложения, а не в момент первого обращения к классу.

Попробуйте сделать так
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
type
  TTest1 = class
  strict private
    class var
      FEncoding: TEncoding;
      FObj: TObject;
  strict private
    class constructor Create;
  public
    class property Encoding: TEncoding read FEncoding;
  end;

class constructor TTest1.Create;
begin
  ShowMessage('Create')
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  LEnc: TObject;
begin
  LEnc := TTest1.Encoding;
end;

а потом замените выделенное на любой другой класс. И расскажите потом о порядке создания

Kazantsev Alexey
Можно репортить.
Голосуем
...
Рейтинг: 0 / 0
Создание TEncoding в классовом конструкторе
    #39968621
Kazantsev Alexey
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
_Vasilisk_
Голосуем

Я уже :)
...
Рейтинг: 0 / 0
Создание TEncoding в классовом конструкторе
    #39968668
shalamyansky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Собрал ваш пример. Заметил следующее - если создать экземпляр вашего класса TTest1, или даже не создавать экземпляр, а хотя бы зарезервировать ссылку для него, например вот так:

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    Test1 : TTest1;
  public
  end;


то пример работает и с TEncoding.

То есть причина не в классе TEncoding как таковом, а в неких зависимостях, которые влияют прежде всего на порядок исполнения начальных вызовов.

Класс же TEncoding не похож на класс TMBCSEncoding и прочие тем, что для него не создается экземпляра. В случае вызова TEncoding.GetEncoding(1251), как у вас, создается экземпляр TMBCSEncoding.

Не знаю, можно ли это считать багом, но поведения хотелось бы более определенного. Отпишитесь, что скажут создатели, любопытно.
...
Рейтинг: 0 / 0
Создание TEncoding в классовом конструкторе
    #39968685
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
shalamyansky
, а хотя бы зарезервировать ссылку для него,
Документация говорит, что код классовых конструкторов вставляется в секцию initialization модуля, где объявлен класс, но только в том случае, если класс используется где-то в программе. А как его использовать: обратиться к методу или объявить переменную - не важно

shalamyansky
Класс же TEncoding не похож на класс TMBCSEncoding и прочие тем, что для него не создается экземпляра.
Для TStream тоже не предусмотрено создание экземпляра класса. Тем не менее с TStream все работает

shalamyansky
Отпишитесь, что скажут создатели,
Из моего опыта общения с багтрекером - ничего не скажут
...
Рейтинг: 0 / 0
Создание TEncoding в классовом конструкторе
    #39968688
shalamyansky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
_Vasilisk_

А как его использовать: обратиться к методу или объявить переменную - не важно

Оказывается, важно. Важно даже, как объявить. Потому что если вот так работает:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    Test1 : TTest1;
  public
end;


То вот так уже нет:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
  public
end;

var
    Test1 : TTest1;



Собственно, я согласен, что это есть баг. Но вы попросили его объяснить, и я выдвинул гипотезу, на мой взгляд правдоподобную. Любая гипотеза нуждается в доказательствах, но пока их нет, я в неё просто верю :)
...
Рейтинг: 0 / 0
Создание TEncoding в классовом конструкторе
    #39968695
alekcvp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
_Vasilisk_
shalamyansky
Класс же TEncoding не похож на класс TMBCSEncoding и прочие тем, что для него не создается экземпляра.
Для TStream тоже не предусмотрено создание экземпляра класса. Тем не менее с TStream все работает

Это что-то новое, что для TStream не предусмотрено создание экземпляра класса.
C TFile и TDirectory тоже работет?
...
Рейтинг: 0 / 0
Создание TEncoding в классовом конструкторе
    #39969398
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
alekcvp
Это что-то новое, что для TStream не предусмотрено создание экземпляра класса.
Так никто TStream.Create и не вызывает. Я говорю о зависимости между объявлением классового свойства и вызова классового конструктора
alekcvp
C TFile и TDirectory тоже работет?
Нет. Может и не должно. TFile это record без полей. Соответственно обращения TFile.Method и TMyClass.File.Method будут всегда эквивалентны и конструктор можно не дергать
...
Рейтинг: 0 / 0
Создание TEncoding в классовом конструкторе
    #39969402
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: pascal
1.
2.
3.
4.
5.
6.
Test.dpr.54: TFile.IntegerToFileAttributes(0);
004D9848 33C0             xor eax,eax
004D984A E8A5BDFFFF       call TFile.IntegerToFileAttributes
Test.dpr.55: TTestOther.File_.IntegerToFileAttributes(0);
004D984F 33C0             xor eax,eax
004D9851 E89EBDFFFF       call TFile.IntegerToFileAttributes


Тогда как при обращении к объекту он ищется у конкретного класса
Код: pascal
1.
2.
3.
Test.dpr.53: LEnc := TTestEncoding.Encoding;
004D988C A1FC3D4E00       mov eax,[$004e3dfc]
004D9891 A3043E4E00       mov [$004e3e04],eax
...
Рейтинг: 0 / 0
Создание TEncoding в классовом конструкторе
    #39969932
_Vasilisk_

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
if TTest1.Obj <> nil then

...
 if TTest1.>>>Encoding<< <> nil then
...

  FObj.Free;
  FEncoding.Free;





А где freeAndNil ? Ты проверяешь что адрес объекта помечен как NIL то free не помечает адрес переменной в nil

И когда объект освобождён, - переменная остаётся со старыми данными.
...
Рейтинг: 0 / 0
Создание TEncoding в классовом конструкторе
    #39969944
rgreat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ВсеРазумный
А где freeAndNil ? Ты проверяешь что адрес объекта помечен как NIL то free не помечает адрес переменной в nil

И когда объект освобождён, - переменная остаётся со старыми данными.


"procedure TForm1.Button1Click(Sender: TObject);" не может быть вызвана после "class destructor TTest1.Destroy;"
...
Рейтинг: 0 / 0
Создание TEncoding в классовом конструкторе
    #39969958
rgreat,

В любом случае баг у меня не воспроизводится
Delphi 10.4 Version 27.0.37889.9797

Memo1
OK: TMBCSEncoding
OK: TObject
OK: TObject
OK: TMBCSEncoding


Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

type
  TTest1 = class
  strict private
  class var
    FEncoding: TEncoding;
    FObj: TObject;
  strict private
    class constructor Create;
    class destructor Destroy;
  public
    class property Encoding: TEncoding read FEncoding;
    class property Obj: TObject read FObj;
  end;

var
  Form1: TForm1;

implementation

class constructor TTest1.Create;
begin
  FEncoding := TEncoding.GetEncoding(1251);
  FObj := TObject.Create;
end;

class destructor TTest1.Destroy;
begin
  FObj.Free;
  FEncoding.Free;
end;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  if TTest1.Obj <> nil then
    Memo1.Lines.Add('OK: ' + TTest1.Obj.ToString)
  else
    Memo1.Lines.Add('Fail');
  if TTest1.Encoding <> nil then
    Memo1.Lines.Add('OK: ' + TTest1.Encoding.ToString)
  else
    Memo1.Lines.Add('Fail');
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  if TTest1.Encoding <> nil then
    Memo1.Lines.Add('OK: ' + TTest1.Encoding.ToString)
  else
    Memo1.Lines.Add('Fail');
  if TTest1.Obj <> nil then
    Memo1.Lines.Add('OK: ' + TTest1.Obj.ToString)
  else
    Memo1.Lines.Add('Fail');
end;

end.

...
Рейтинг: 0 / 0
Создание TEncoding в классовом конструкторе
    #39970155
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ВсеРазумный
В любом случае баг у меня не воспроизводится
Тест некорректный. Ты обращаясь к TTest.Obj провоцируешь вызов конструктора. Попробуй так
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
type
  TTest1 = class
  strict private
  class var
    FEncoding: TEncoding;
  strict private
    class constructor Create;
    class destructor Destroy;
  public
    class property Encoding: TEncoding read FEncoding;
  end;

  TTest2 = class
  strict private
  class var
    FObj: TObject;
  strict private
    class constructor Create;
    class destructor Destroy;
  public
    class property Obj: TObject read FObj;
  end;
...........
procedure TForm1.Button2Click(Sender: TObject);
begin
  if TTest1.Encoding <> nil then
    Memo1.Lines.Add('OK: ' + TTest1.Encoding.ToString)
  else
    Memo1.Lines.Add('Fail');
  if TTest2.Obj <> nil then
    Memo1.Lines.Add('OK: ' + TTest1.Obj.ToString)
  else
    Memo1.Lines.Add('Fail');
end;
...
Рейтинг: 0 / 0
19 сообщений из 19, страница 1 из 1
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Создание TEncoding в классовом конструкторе
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]