TValue
类型为
TNotifyEvent
始终是
void (__closure *)(class System::TObject *) __attribute__((fastcall))
。我创建了辅助方法,可以在运行时协助设置事件,但不断收到 EInvalidCast 消息“无效的类类型转换”。
void __fastcall setProperty(TObject * component, const String name, TValue value)
{
auto * context = getContext(); // TRttiContext *
auto * property = context->GetType(component->ClassType())->GetProperty(name);
volatile String valueType = AnsiString(value.TypeInfo->Name); // "void (__closure *)(class System::TObject *) __attribute__((fastcall))"
volatile String propertyType = property->PropertyType->Name; // "TNotifyEvent"
volatile String oldType = AnsiString(property->GetValue(component).TypeInfo->Name); // "TNotifyEvent"
property->SetValue(component, value); // Raising exception
}
主叫方:
btn 是 TButton *
auto value = TValue::From<TNotifyEvent>(&closeBtnClick);
setProperty(btn, "OnClick", value);
还尝试根据这个问题使用TMethod,但引发了相同的异常: 上下文是 TRttiContext *
auto * method = context->GetType(__classid(TStandardPanel))->GetMethod("closeBtnClick");
TMethod * handler = new TMethod();
handler->Code = method->CodeAddress;
handler->Data = this;
auto value = TValue::From(TNotifyEvent(handler));
setProperty(btn, "OnClick", value);
但是当我直接分配它时它就起作用了:
btn->OnClick = &closeBtnClick;
如果从其他属性获取 TValue 并重新分配给当前属性,它也可以工作。
TNotifyEvent
是 C++ 中的
typedef
。它不是具有自己的 RTTI 的独特类型。 它只是一个别名,这就是为什么您会看到它的类型报告为
void (__closure *)(class System::TObject *) __attribute__((fastcall))
,因为这是 C++ 所看到的实际类型。这与 Delphi 不同,其中
TNotifyEvent
是一种具有自己的 RTTI 的独特类型。您的
TMethod
尝试是错误的。摆脱
new
,您对
TMethod*
指针的类型转换是错误的。 你需要更多类似这样的东西:
TMethod handler;
handler.Code = method->CodeAddress;
handler.Data = this;
auto value = TValue::From(reinterpret_cast<TNotifyEvent&>(handler));
或者:
auto value = TValue::From(*reinterpret_cast<TNotifyEvent*>(&handler));
或者:
TNotifyEvent handler;
TMethod &m = reinterpret_cast<TMethod&>(handler);
m.Code = method->CodeAddress;
m.Data = this;
auto value = TValue::From(handler);
但是,在任何这些情况下,您总是会得到 TValue
类型作为
void (__closure *)(class System::TObject *) __attribute__((fastcall))
,因为这是 C++ 端
TNotifyEvent
的真实类型。因此,要做您正在尝试的事情,您需要 Delphi RTTI
TNotifyEvent
。 您可以从
TRttiContext::FindType()
获取它,然后将其传递给
TValue::Make()
,例如:
PTypeInfo GetTNotifyEventTypeInfo()
{
auto * context = getContext();
auto * type = context->FindType(_D("System.Classes.TNotifyEvent"));
return type->Handle;
}
...
auto handler = closeBtnClick;
TValue value;
TValue::Make(&handler, GetTNotifyEventTypeInfo(), value);
setProperty(btn, _D("OnClick"), value);
但是,更通用(且正确)的解决方案是从您尝试分配的实际属性中查询 TypeInfo,例如:
PTypeInfo GetPropertyTypeInfo(TObject * component, const String name)
{
auto * context = getContext();
auto * type = context->GetType(component->ClassType());
auto * property = type->GetProperty(name);
return property->PropertyType->Handle;
}
...
auto handler = closeBtnClick;
TValue value;
TValue::Make(&handler, GetPropertyTypeInfo(btn, _D("OnClick")), value);
setProperty(btn, _D("OnClick"), value);