В Delphi, как можно проверить, реализует ли ссылка IInterface полученный, но не явно поддерживаемый интерфейс?

Ну, вы можете начать с этого:

dfR = df1.copy()
dfR.loc[dfR['TranDate'].between(df2['StartDate'], df2['EndDate']), 'PP'] = df2['PP']

Может быть, это сработает для вас.

12
задан David 16 April 2009 в 16:27
поделиться

3 ответа

Одна вещь, которую вы можете сделать, это остановить интерфейсы приведения типов . Вам не нужно делать это, чтобы перейти от IDerived к IBase , и вам не нужно это, чтобы перейти от IBase к IUnknown , либо. Любая ссылка на IDerived уже является IBase , поэтому вы можете вызывать методы IBase даже без приведения типов. Если вы выполняете меньше приведения типов, вы позволяете компилятору выполнять больше работы за вас и ловить несуществующие вещи.

Ваша заявленная цель состоит в том, чтобы проверить, действительно ли то, что вы выводите из списка, действительно является ссылкой IBase . Добавление IBase в качестве реализованного интерфейса позволит вам легко достичь этой цели. В этом свете ваши "две основные причины" за то, что не сделал этого, не держите воду.

  1. «Я хочу иметь возможность сравнивать ссылки на равенство»: нет проблем. COM требует, чтобы, если вы дважды вызывали QueryInterface с одним и тем же GUID для одного и того же объекта, вы оба раза получали один и тот же указатель интерфейса. Если у вас есть две произвольные ссылки на интерфейс, и вы как представили их обе IBase , тогда результаты будут иметь одно и то же значение указателя, если и только если они поддерживаются одним и тем же объектом .

    Поскольку вы, кажется, хотите, чтобы в вашем списке содержались только значения IBase , и у вас нет Delphi 2009, где будет полезен универсальный TInterfaceList , вы можете дисциплинируйте себя, чтобы всегда явно добавлять значения IBase в список, а не значения любого потомка.

    Таким образом, любые дубликаты в списке легко обнаружить, и ваши «жёсткие броски» гарантированно сработают.

  2. «Это на самом деле не решает проблему»: но это так, учитывая правило выше.

     Assert (Поддерживает (List [i], IBase));
    

    Когда объект явно реализует все свои интерфейсы, вы можете проверить наличие подобных вещей. И если вы добавили элементы в список, как я описал выше, это утверждение можно отключить. Включение утверждения позволяет вам определить, когда кто-то изменил код в другом месте вашей программы, чтобы добавить элемент в список неправильно. Частое выполнение ваших модульных тестов позволит вам обнаружить проблему очень скоро и после ее появления.

Имея в виду вышеуказанные моменты, вы можете проверить, что все, что было добавлено в список, было добавлено правильно с помощью этого кода:

var
  AssertionItem: IBase;

Assert(Supports(List[i], IBase, AssertionItem)
       and (AssertionItem = List[i]));
// I don't recall whether the compiler accepts comparing an IBase
// value (AssertionItem) to an IUnknown value (List[i]). If the
// compiler complains, then simply change the declaration to
// IUnknown instead; the Supports function won't notice.

Если утверждение не выполнено, то либо вы добавили что-то в список, который вообще не поддерживает IBase , либо конкретная ссылка на интерфейс, добавленная вами для какого-либо объекта, не может служить ссылкой IBase , Если утверждение проходит, тогда вы знаете, что List [i] даст вам действительное значение IBase .

Обратите внимание, что добавляемое в список значение не требует для явным образом быть значением IBase . Учитывая ваши объявления типов выше, это безопасно:

var
  A: IDerived;
begin
  A := TImplementation.Create;
  List.Add(A);
end;

Это безопасно, потому что интерфейсы, реализованные в TImplementation , образуют дерево наследования, которое вырождается в простой список. Нет ветвей, в которых два интерфейса не наследуют друг от друга, но имеют общего предка. Если бы было два потомка IBase и TImplementation реализовали их обоих, приведенный выше код был бы недействительным, поскольку ссылка IBase проводится в не обязательно будет "каноническим" IBase ссылка для этого объекта. Утверждение обнаружит эту проблему, и вам нужно будет добавить его с помощью List.Add (A as IBase) .

Когда вы отключаете утверждения, стоимость правильных типов оплачивается только при добавлении в список, а не при чтении из списка. Я назвал переменную AssertionItem , чтобы отговорить вас использовать эту переменную в другом месте процедуры; он только поддерживает утверждение и не будет иметь действительного значения, когда утверждения отключены.

Я назвал переменную AssertionItem , чтобы отговорить вас использовать эту переменную в другом месте процедуры; он только поддерживает утверждение и не будет иметь действительного значения, когда утверждения отключены.

Я назвал переменную AssertionItem , чтобы отговорить вас использовать эту переменную в другом месте процедуры; он только поддерживает утверждение и не будет иметь действительного значения, когда утверждения отключены.

12
ответ дан 2 December 2019 в 19:32
поделиться

В Test2;

Вы не должны повторно вводить ID, как IBase (IB) (A), но с:

Supports(A, IBase, B);

А добавить в список можно просто:

List.Add(B);
0
ответ дан 2 December 2019 в 19:32
поделиться

Вы правы на экзамене и, насколько я могу Скажите, что на самом деле нет прямого решения проблемы, с которой вы столкнулись. Причины кроются в природе наследования между интерфейсами, которая имеет лишь смутное сходство наследования между классами. Унаследованные интерфейсы - это совершенно новый интерфейс, имеющий несколько общих методов с интерфейсом, от которого он наследуется, но не имеет прямого соединения. Поэтому, решив не реализовывать интерфейс базового класса, вы делаете конкретное предположение, что скомпилированная программа будет следовать: TImplementation не реализует IBase. Я думаю, что «наследование интерфейса» является неправильным, расширение интерфейса имеет больше смысла! Обычной практикой является наличие базового класса, реализующего базовый интерфейс, а не производных классов, реализующих расширенные интерфейсы, но в случае, если вам нужен отдельный класс, реализующий оба, просто перечислите эти интерфейсы. Есть ли конкретная причина, по которой вы хотите избегать использования:

TImplementation = Class(TInterfacedObject, IDerived, IBase)

или просто она вам не нравится?

Дальнейший комментарий

Вы никогда не должны, даже вживую, приводить интерфейс. Когда вы делаете «как» в интерфейсе, он корректно корректирует указатели vtable объекта ... если вы выполняете жесткую приведение (и у вас есть методы для вызова), ваш код может легко потерпеть крах. У меня сложилось впечатление, что вы относитесь к интерфейсам как к объектам (используя наследование и приведение одинаково), в то время как их внутренняя работа действительно отличается!

6
ответ дан 2 December 2019 в 19:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: