Интерфейсная ловушка Delphi
Теперь я знаю две основные ошибки:
Ловушка 1. ловушка преобразования типа интерфейса
A) Вы не можете отменить ссылку на объект на интерфейс, который не объявляет тип этой ссылки, даже если объект фактически реализует этот интерфейс (хе -хе, преимущества трудно произнести). б) Когда переменная объекта назначена переменной интерфейса и когда переменная интерфейса назначена переменной объекта, адрес переменной объекта изменился, то есть он больше не является исходным объектом, но указывает на адрес ошибки Полем Например: i1 = интерфейс
Функция DO: Boolean;
конец;
TC1 = класс
ATT1: целое число;
конец;
TC2 = класс (TC1, I1)
ATT2: целое число;
Функция DO: Boolean;
end; intf1: i1; obj1: tc !; obj2: tc2; obj2: = tc2.create;
Obj1: = obj2.
I1 (obj2) .do;
I1 (obj1) .do; Поскольку тип OBJ1 TC1 не заявляет, что I1 реализован, он не может быть преобразован в I1, даже если OBJ1 реализует I1. Кроме того, если вы конвертируете объект в интерфейс, а затем обратно будут проблемы. Obj2: = tc2.create; obj2.att1: = 0;
Intf1: = obj2; // Правильно. Obj2: = intf1; Obj2.att1: = 0; // Незаконная ошибка доступа к адресу во время выполнения. То есть после преобразования из ссылки на объект в ссылку на указатель изменяется адрес, но когда ссылка указателя возвращается из ссылки на объект, а затем возвращается в ссылку на объект (ошибка Delphi?).
Ловушка 2. Управление сроком службы интерфейса
Основываясь на моем здравом смысле (здесь - это программирование здравого смысла, а не Delphi, используя здравый смысл), я думаю, что интерфейсы не требуют управления сроком службы, потому что интерфейсы вообще не могут генерировать реальные объекты. Но Delphi еще раз поразил мой здравый смысл (да, почему вы говорите «обновление»?), Его интерфейс имеет время жизни, и он должен реализовать следующие три метода: функциональный QueryInterface (const iid: tguid; out obj): hResult; STDCALL; Так что я не знаю, как реализовать эти три метода. J Если вы не хотите реализовать эти три метода самостоятельно, вы можете использовать TCOMPONENT. Поскольку TComponent реализовал эти три метода, он может быть унаследован от него, поэтому нет необходимости реализовать эти три метода. Можете ли вы использовать его с уверенностью? Ответ нет. Потому что Дельфи тайно (потому что это было за пределами моих ожиданий), когда вы устанавливаете переменную интерфейса на ноль. Функция _intfclear (var dest: iinterface): var p: pointer; ) ._Release; Функция tcomponent._release: Integer; , тогда ничего не будет не делать. Что мы не работаем как COM -объекты, так что нет проблем? Ответ по -прежнему нет, рассмотрим следующую ситуацию: obj2: = tc2.create; tryintf1: = obj2; intf1.do; Нелегальная ошибка доступа к адресу возникнет. Почему? Как упомянуто выше, когда будет вызван ссылка на интерфейс на NIL, будет вызван _intfclear, и _intfClear будет вызвать _Erelease объекта. Некоторые люди говорят, что это ненужное? Obj2: = tc2.create; tryintf1: = obj2; intf1.do; Почему? Поскольку компилятор Delphi умный, он думает, что вы забыли установить эту ссылку на адрес NIL, поэтому вы автоматически добавите его к вам. Как это решить? Метод 1: Сначала установите ссылку на интерфейс на NIL, а затем выпустите объект. Intf1: = nil; Указатель (Intf1): = NIL; Я склонен использовать второй метод, поэтому вам не нужно думать о том, кого сначала выпустить. Более того, в некоторых шаблонах дизайна вы можете иметь только ссылки на интерфейс, и вы не знаете, когда будет выпущен ссылочный объект. Например, рассмотрим композитный шаблон. Tcomposite = class (tcomponent, i1) частные интерфейсы: txcontainer; // класс контейнеров, который хранит ссылку на интерфейс для «листа». Общественная процедура добавить (aintf: i1); Очевидно, нет, так ли определенно будут выпущены «листья» позже, чем этот объект «объект синтеза»? Я так не думаю. Если это регулирование обязано, много гибкости будет потеряно. Таким образом, мы определенно думаем, что когда эти ссылки на интерфейс будут размещены NIL, не будет никаких отношений с исходным объектом, чтобы избежать незаконных ошибок доступа к адресу после выпуска объекта. Какой контейнер вы должны использовать? множество? TLIST? Tinterfacelist? Прежде всего, я думаю, что это должен быть Tinterfacelist, потому что то, что мы хотим разместить, - это интерфейс. Но когда он свободен, он ставит все интерфейсы, которые он размещает в NIL, что именно то, чего мы не хотим. Или мы можем преобразовать ссылку на интерфейс, который он хранит в указатель, прежде чем свободный, а затем установить его на ноль. для i: = 0, чтобы extlist.count -1 dopointer (interlist.items [i]): = nil; Затем мы пробуем массив. Вмешивание: массив I1; динамические массивы не должны быть выпущены. Это кажется очень полезным, но когда компилятор выпускает его, он все равно будет устанавливать каждый элемент на ноль, и в качестве интерфейса все еще существует вероятность незаконных ошибок доступа к адресу. Это может быть сделано таким образом для i: = low (arr) до высокого (ARR) DoPointer (arr [i]): = nil; Время, когда вы используете его, и вы не помните, чтобы сделать это или нет. Наконец, используйте tlist. Тем не менее, есть указатель в TLIST, поэтому при добавлении вы должны выполнить следующее: xxx.add (aintf: i1) начинать интерфейс. нуждается в специальной обработке при выпуске. Кажется, это идеально, но все еще есть ловушка. interlist.add (tc2.create); Поскольку это чистый указатель, при преобразовании в интерфейс преобразование адреса объекта, на которое ссылается ссылка на интерфейс, не выполняется (он не знает, как это сделать), поэтому при вызове метода, объявленного интерфейсом, Это еще одна незаконная ошибка доступа к адресу. Это единственный способ написать: interlist.add (pointer (i1 (tc2.create)); хотя это немного хлопотно, это лучшее решение (как я знаю). Потому что, если вы забудете, что передача допускает ошибку при первом вызове, легче найти ошибки (по сравнению с использованием массива). Поэтому я предлагаю: 1. Используйте TLIST для управления ссылками на интерфейс. 2. При добавлении вы должны преобразовать объект в интерфейс, а затем в указатель. 3. Для ссылок на интерфейс, которые не управляются с использованием TLIST. Для ссылки на интерфейсы вы должны вручную установить на NIL по следующему методу: Pointer (intfref): = NIL; Следовательно, унаследование от этого также может спасти себя проблемой в реализации этих трех методов. Но его _ERELEASE реализуется так: функция TinterFacedObject._Release: Integer; Объект. Вот как он реализован, он приносит больше проблем, чем наследство от TComponent. Если не используется иное, это не рекомендуется. Все дискуссии по интерфейсам выше ограничены интерфейсами на уровне языка, используемых в целом, и не обсуждается по интерфейсам COM+. Реализация этих странных интерфейсов Delphi во многом связана с его развитием из интерфейса COM+, что является результатом компромисса, сделанного из -за тяжелого исторического бремени.