Имейте в виду, что независимо от сценария причина всегда одинакова в .NET:
Вы пытаетесь использовать ссылочную переменную, значение которой
Nothing
/null
. Если для ссылочной переменной значениеNothing
/null
, это означает, что на самом деле оно не содержит ссылку на экземпляр любого объекта, который существует в куче.Вы либо никогда не присваивали какую-либо переменную, никогда не создавали экземпляр значения, присвоенного переменной, или вы вручную устанавливали переменную, равную
blockquote>Nothing
/null
, или вы вызывали функцию, которая установите для этой переменной значениеNothing
/null
.
Для использования обычного программирования ООП вы всегда должны использовать вид class
. У вас будет самая мощная объектная модель в Delphi, включая интерфейс и дженерики (в более поздних версиях Delphi).
1. Записи, указатели и объекты
Записи могут быть злыми (медленная скрытая копия, если вы забыли объявить параметр как const
, запишите скрытый код медленной очистки, fillchar
заставить любую строку в записи стать утечкой памяти ...), но иногда очень удобно обращаться к двоичной структуре (например, к некоторому «маленькому значению») через указатель.
Динамический массив крошечных записи (например, с одним целым числом и одним двойным полем) будут намного быстрее, чем TList
малых классов; с нашей TDynArray
оберткой , вы получите доступ к записям высокого уровня, с сериализацией, сортировкой, хэшированием и т. д.
. Если вы используете указатели, вы должны знать, что вы делаем. Определенно предпочтительнее придерживаться классов и TPersistent
, если вы хотите использовать магическую «модель владения компонентом VCL».
Наследование не допускается для записей. Вам нужно либо использовать «вариантную запись» (используя ключевое слово case
в определении типа), либо использовать вложенные записи. При использовании C-like API вам иногда приходится использовать объектно-ориентированные структуры. Использование вложенных записей или вариантных записей ИМХО гораздо менее понятно, чем старая «натуральная» модель наследования.
2. Когда использовать объект
Но есть места, где объекты являются хорошим способом доступа к уже имеющимся данным.
Даже объектная модель лучше новой модели записи, поскольку она обрабатывает простое наследование.
В записи блога прошлым летом я разместил некоторые возможности для использования объектов:
3. Как реализуется реализация объекта в современном Delphi
Тот факт, что объект нарушен в современном Delphi, является позором IMHO.
Обычно, если вы определяете запись в стеке, содержащую некоторые (например, строка), он будет инициализирован некоторым магическим кодом компилятора на начальном уровне метода / функции:
type TObj = object Int: integer; Str: string; end;
procedure Test;
var O: TObj
begin // here, an _InitializeRecord(@O,TypeInfo(TObj)) call is made
O.Str := 'test';
(...)
end; // here, a _FinalizeRecord(@O,TypeInfo(TObj)) call is made
Те _InitializeRecord
и _FinalizeRecord
будут " подготовьте "затем" освободите "переменную O.Str.
В Delphi 2010 я обнаружил, что иногда этот _InitializeRecord () не всегда был выполнен. Если в записи нет только каких-либо открытых полей, скрытые вызовы иногда не генерируются компилятором.
Просто создайте источник еще раз, и будет ...
Единственный Решение, которое я обнаружил, использовало ключевое слово записи вместо объекта.
Итак, вот как выглядит полученный код:
/// used to store and retrieve Words in a sorted array
// - is defined either as an object either as a record, due to a bug
// in Delphi 2010 compiler (at least): this structure is not initialized
// if defined as a record on the stack, but will be as an object
TSortedWordArray = {$ifdef UNICODE}record{$else}object{$endif}
public
Values: TWordDynArray;
Count: integer;
/// add a value into the sorted array
// - return the index of the new inserted value into the Values[] array
// - return -(foundindex+1) if this value is already in the Values[] array
function Add(aValue: Word): PtrInt;
/// return the index if the supplied value in the Values[] array
// - return -1 if not found
function IndexOf(aValue: Word): PtrInt; {$ifdef HASINLINE}inline;{$endif}
end;
{$ifdef UNICODE}record{$else}object{$endif}
ужасно ... но ошибка генерации кода не произошла, поскольку ..
Полученные изменения в исходном коде не огромны, но немного разочаровывают. Я узнал, что более старая версия IDE (например, Delphi 6/7) не может анализировать такое объявление, поэтому иерархия классов будет разбита в редакторе ...: (
Обратная совместимость должна включать регрессионные тесты Многие пользователи Delphi остаются в этом продукте из-за существующего кода. Ломающиеся функции очень проблематичны для будущего Delphi, ИМХО: если вам нужно переписать много кода, то вам не следует просто переключать проект на C # или Java?
Вернувшись в более старые версии Delphi, которые не поддерживали записи с помощью методов, тогда использование object
было способом получить ваши объекты, выделенные в стеке. Очень редко это могло бы принести пользу производительности. В настоящее время record
лучше. Единственная функция, отсутствующая в record
, - это способность наследовать от другого record
.
Вы отказываетесь от многого, когда вы изменяетесь с class
на record
, поэтому учитывайте это только в том случае, если выгоды от производительности являются подавляющими.
Object
не был методом Delphi 1 для настройки объектов; это был недолговечный метод Turbo Pascal по настройке объектов, который был заменен моделью Delphi TObject в Delphi 1. Он поддерживался для обратной совместимости, но его следует избегать по нескольким причинам:
object
, чтобы не потерять ничего здесь. Что касается остальной части вопроса, есть не так много преимуществ по скорости. Модель TObject довольно быстро, особенно если вы используете диспетчер памяти FastMM для ускорения создания и уничтожения объектов, и если ваши объекты содержат много полей, они могут быть даже быстрее, чем записи во многих случаях, re передается по ссылке и не нужно копировать для каждого вызова функции.
Если задан выбор между «быстрым и, возможно, сломанным» и «быстрым и правильным», всегда выбирайте последнее.
Объекты в старом стиле не предлагают стимулов скорости по сравнению с обычными старыми записями, поэтому, где бы вы ни находились у вас возникнет соблазн использовать объекты старого стиля, вы можете использовать записи вместо этого, не рискуя иметь неинициализированные типы управляемых компиляторами или разбитые виртуальные методы. Если ваша версия Delphi не поддерживает записи с помощью методов, просто используйте автономные процедуры.