Ну, вы можете начать с этого:
dfR = df1.copy()
dfR.loc[dfR['TranDate'].between(df2['StartDate'], df2['EndDate']), 'PP'] = df2['PP']
Может быть, это сработает для вас.
Одна вещь, которую вы можете сделать, это остановить интерфейсы приведения типов . Вам не нужно делать это, чтобы перейти от IDerived
к IBase
, и вам не нужно это, чтобы перейти от IBase
к IUnknown
, либо. Любая ссылка на IDerived
уже является IBase
, поэтому вы можете вызывать методы IBase
даже без приведения типов. Если вы выполняете меньше приведения типов, вы позволяете компилятору выполнять больше работы за вас и ловить несуществующие вещи.
Ваша заявленная цель состоит в том, чтобы проверить, действительно ли то, что вы выводите из списка, действительно является ссылкой IBase
. Добавление IBase
в качестве реализованного интерфейса позволит вам легко достичь этой цели. В этом свете ваши "две основные причины" за то, что не сделал этого, не держите воду.
«Я хочу иметь возможность сравнивать ссылки на равенство»: нет проблем. COM требует, чтобы, если вы дважды вызывали QueryInterface
с одним и тем же GUID для одного и того же объекта, вы оба раза получали один и тот же указатель интерфейса. Если у вас есть две произвольные ссылки на интерфейс, и вы как
представили их обе IBase
, тогда результаты будут иметь одно и то же значение указателя, если и только если они поддерживаются одним и тем же объектом .
Поскольку вы, кажется, хотите, чтобы в вашем списке содержались только значения IBase
, и у вас нет Delphi 2009, где будет полезен универсальный TInterfaceList
, вы можете дисциплинируйте себя, чтобы всегда явно добавлять значения IBase
в список, а не значения любого потомка.
Таким образом, любые дубликаты в списке легко обнаружить, и ваши «жёсткие броски» гарантированно сработают.
«Это на самом деле не решает проблему»: но это так, учитывая правило выше.
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
, чтобы отговорить вас использовать эту переменную в другом месте процедуры; он только поддерживает утверждение и не будет иметь действительного значения, когда утверждения отключены. В Test2;
Вы не должны повторно вводить ID, как IBase (IB) (A), но с:
Supports(A, IBase, B);
А добавить в список можно просто:
List.Add(B);
Вы правы на экзамене и, насколько я могу Скажите, что на самом деле нет прямого решения проблемы, с которой вы столкнулись. Причины кроются в природе наследования между интерфейсами, которая имеет лишь смутное сходство наследования между классами. Унаследованные интерфейсы - это совершенно новый интерфейс, имеющий несколько общих методов с интерфейсом, от которого он наследуется, но не имеет прямого соединения. Поэтому, решив не реализовывать интерфейс базового класса, вы делаете конкретное предположение, что скомпилированная программа будет следовать: TImplementation не реализует IBase. Я думаю, что «наследование интерфейса» является неправильным, расширение интерфейса имеет больше смысла! Обычной практикой является наличие базового класса, реализующего базовый интерфейс, а не производных классов, реализующих расширенные интерфейсы, но в случае, если вам нужен отдельный класс, реализующий оба, просто перечислите эти интерфейсы. Есть ли конкретная причина, по которой вы хотите избегать использования:
TImplementation = Class(TInterfacedObject, IDerived, IBase)
или просто она вам не нравится?
Дальнейший комментарий
Вы никогда не должны, даже вживую, приводить интерфейс. Когда вы делаете «как» в интерфейсе, он корректно корректирует указатели vtable объекта ... если вы выполняете жесткую приведение (и у вас есть методы для вызова), ваш код может легко потерпеть крах. У меня сложилось впечатление, что вы относитесь к интерфейсам как к объектам (используя наследование и приведение одинаково), в то время как их внутренняя работа действительно отличается!