爱,代码+喵星人
引言
上篇章节总结了类型系统的最后一些小知识点,为了免于说都是纯理论知识,本篇我们来讲一些利用反射的例子。
获取类型对象
如果想获取到程序里定义的所有的class,方便的方法是:
TArray<UObject*> result;
GetObjectsOfClass(UClass::StaticClass(), result); //获取所有的class和interface
GetObjectsOfClass(UEnum::StaticClass(), result); //获取所有的enum
GetObjectsOfClass(UScriptStruct::StaticClass(), result); //获取所有的struct
GetObjectsOfClass是UE4已经写好的一个很方便的方法,可以获取到属于某个UClass*下面的所有对象。因此如果用UClass::StaticClass()本身,就可以获得程序里定义的所有class。值得注意的是,UE4里的接口是有一个配套的UInterface对象来存储元数据信息,它的类型也是用UClass*表示的,所以也会获得interface。根据前文,enum会生成UEnum,struct会生成UScriptStruct,所以把参数换成UEnum::StaticClass()就可以获得所有的UEnum*对象了,UScriptStruct::StaticClass()就是所有的UScriptStruct*了,最后就可以根据这些类型对象来反射获取类型信息了。
而如果要精确的根据一个名字来查找某个类型对象,就可以用UE4里另一个方法:
template< class T >
inline T* FindObject( UObject* Outer, const TCHAR* Name, bool ExactClass=false )
{
return (T*)StaticFindObject( T::StaticClass(), Outer, Name, ExactClass );
}
UClass* classObj=FindObject<UClass>(ANY_PACKAGE,"MyClass"); //获得表示MyClass的UClass*
这样就可以轻易的获得不同类型的对象。FindObject内部的原理在下大章节内存管理再讲述。
遍历字段
在获取到了一个类型对象后,就可以用各种方式去遍历查找内部的字段了。为此,UE4提供了一个方便的迭代器TFieldIterator<T>,可以通过它筛选遍历字段。
const UStruct* structClass; //任何复合类型都可以
//遍历属性
for (TFieldIterator<UProperty> i(structClass); i; ++i)
{
UProperty* prop=*i;
}
//遍历函数
for (TFieldIterator<UFunction> i(structClass); i; ++i)
{
UFunction* func=*i;
//遍历函数的参数
for (TFieldIterator<UProperty> i(func); i; ++i)
{
UProperty* param=*i;
if( param->PropertyFlags & CPF_ReturnParm ) //这是返回值
{
}
}
}
//遍历接口
const UClass* classObj; //只有UClass才有接口
for (const FImplementedInterface& ii : classObj->Interfaces)
{
UClass* interfaceClass = ii.Class;
}
给模板参数T传UFunction就可以获得类型下的所有函数,通过这也可以遍历获得UFunction下的参数列表。当然TFieldIterator也可以再传其他参数的控制是否包含基类的字段、是否包含废弃的字段、是否包含接口里的字段。TFieldIterator的内部实现其实也挺简单的,一是通过SuperStruct来获得Super,二是通过Interfaces来获得实现的接口,三是用Field->Next来遍历字段。信息数据都是全的,迭代遍历就简单了。
遍历枚举的字段也很简单:
const UEnum* enumClass;
for (int i = 0; i < enumClass->NumEnums(); ++i)
{
FName name = enumClass->GetNameByIndex(i);
int value = enumClass->GetValueByIndex(i);
}
还有遍历元数据的:
#if WITH_METADATA
const UObject* obj;//可以是任何对象,但一般是UField才有值
UMetaData* metaData = obj->GetOutermost()->GetMetaData();
TMap<FName, FString>* keyValues = metaData->