UE5.4 通过反射实现子类到参数类之间的转化
1. 函数参数是父类指针,那该怎么得到子类的独有属性呢?
可以通过反射的特性得到子类的特有属性,需要注意的是,使用GetClass获得的是该子类的属性,而使用StaticClass获得的就是父类的属性了。
class UAClass : public UObject
{
GENERATED_UCLASS_BODY()
FColor AColor;
virtual void Func();
}
class UAClass_ChildA : public UAClass
{
GENERATED_BODY()
public:
int nameId;
virtual void Func() override;
}
class UAClass_ChildB: public UAClass
{
GENERATED_BODY()
public:
FColor MyColor;
virtual void Func() override;
}
//在传入参数是父类的情况下,应该如何获得子类的特有属性
void FuncA(const UAClass AClass)
{
UClass* Parent = AClass::StaticClass();
for (TFieldIterator<FProperty> PropIt(AClass->GetClass()); PropIt; ++PropIt)
{
FProperty* Property = *PropIt;
FName PropertyName = Property->GetFName();
bool bIsInParentClass = Parent->FindPropertyByName(Property->GetFName()) != nullptr;
if(!bIsInParentClass)
{
UE_LOG(LogTemp, Warning, TEXT("Property:%s"), *PropertyName.ToString());
}
}
}
2. 函数参数传入的是父类,如何根据子类属性为不知道具体结构的FInstancedStruct赋值呢?
FInstancedStruct
是 Unreal Engine(虚幻引擎)中一个用于表示实例化结构体的类。它允许将不同类型的结构体实例存储在单一容器中,并在运行时对其进行操作。这在处理多态结构体或需要在运行时动态改变结构体类型的情况下特别有用,但函数参数为FInstanceStruct时,我们很难知道其中的具体结构。
USTRUCT()
struct FExampleStructA
{
GENERATED_BODY()
UPROPERTY()
int32 ValueA;
};
USTRUCT()
struct FExampleStructB
{
GENERATED_BODY()
UPROPERTY()
float ValueB;
};
void ExampleUsage()
{
// 创建一个 FInstancedStruct 实例
FInstancedStruct InstancedStruct;
// 将 FExampleStructA 实例化并存储在 InstancedStruct 中
InstancedStruct.InitializeAs<FExampleStructA>();
FExampleStructA* StructA = InstancedStruct.GetMutable<FExampleStructA>();
StructA->ValueA = 42;
// 重新初始化 InstancedStruct 为 FExampleStructB
InstancedStruct.InitializeAs<FExampleStructB>();
FExampleStructB* StructB = InstancedStruct.GetMutable<FExampleStructB>();
StructB->ValueB = 3.14f;
}
利用反射的特性,可以通过GetScriptStruct()
来获取其中的属性,这里需要注意的是,使用StaticStruct
是无法获得具体属性的。
const UScriptStruct* ScriptStruct = InstancedStruct->GetScriptStruct();
for (TFieldIterator<FProperty> PropIt(ScriptStruct); PropIt; ++PropIt)
{
FProperty* Property = *PropIt;
FName PropertyName = Property->GetFName();
UE_LOG(LogTemp, Warning, TEXT("Property:%s"), *PropertyName.ToString());
}
那么要如何根据子类中独有的特性为传入的FInstancedStruct
进行赋值呢?
(ps:这里的FInstancedStruct
也是已经确定好结构的,只是我们并不知道其中的具体参数和数值)
由GetScriptStruct()
获得的结构是不包含数值的,无法更改其属性值,因此,我们可以通过GetMutableMemory()
来获取FInstancedStruct
对应的内存地址,从而对其中的属性进行更改。
最终的代码如下:
//在传入参数是父类的情况下,且要赋值的结构为FInstancedStruct
void FuncA(const UAClass AClass, FInstancedStruct* Params)
{
UClass* Parent = AClass::StaticClass();
// 获取FInstancedStruct的具体结构
const UScriptStruct* ScriptStruct = InstancedStruct->GetScriptStruct();
// 获取FInstancedStruct的可变内存地址
void* StructData = Params->GetMutableMemory();
for (TFieldIterator<FProperty> PropIt(AClass->GetClass()); PropIt; ++PropIt)
{
FProperty* Property = *PropIt;
FName PropertyName = Property->GetFName();
bool bIsInParentClass = Parent->FindPropertyByName(Property->GetFName()) != nullptr;
if(!bIsInParentClass)
{
FProperty* BProperty = ScriptStruct->FindPropertyByName(PropertyName);
if (BProperty)
{
const void* AValue = Property->ContainerPtrToValuePtr<void>(&AClass);
void* BValue = BProperty->ContainerPtrToValuePtr<void>(StructData);
if (AValue && BValue)
{
BProperty->CopyCompleteValue(BValue, AValue);
}
}
}
}
}