【UEC++学习】反射编程基础

一个类想要拥有反射的话需要包含以下内容
(1)UCLASS()前缀宏
(2)类名要加上合理的命名,如AU
(3)对应类名合理的继承,如继承AActorUClass
(4)加入对应的反射头文件,XXX.generated.h
(5)加入反射所对应的宏,GENERATED_BODY()

反射的调用应包含在 GENERATED_UCLASS_BODY()宏声明下,该声明下自带反射的构造函数如下:

AReflectionGameMode::AReflectionGameMode(const FObjectInitializer& ObjectInitializer) : AGameModeBase(ObjectInitializer)
{
    
    
	// 你的反射代码
}

1. 获取UClass

/** 通过创建对象获取Class */
UReflectionTestClass* TestObject = NewObject<UReflectionTestClass>();
UClass* TestClass = TestObject->GetClass();
/** 直接获取静态类 */
UClass* TestClass = UReflectionTestClass::StaticClass();

2. 获取类名

/** 获取类名 */
FName ClassName = TestClass->GetFName();

3. 获取类的成员变量

/** 获取类的成员变量 */
for(FProperty* Property = TestClass->PropertyLink; Property; Property = Property->PropertyLinkNext)
{
    
    
	// 获取成员变量的名子
	FString PropertyName = Property->GetName();
	// 获取成员变量的类型
	FString PropertyType = Property->GetCPPType();
	if(PropertyType == "FString")
	{
    
    
		// 强制转化为String
		FStrProperty* StringProperty = CastField<FStrProperty>(Property);
		// 传入某一个对象,表示获取哪一个对象的成员变量值,得到地址
		void* Addr = StringProperty->ContainerPtrToValuePtr<void>(TestObject);
		// 通过地址获取成员变量值
		FString PropertyValue = StringProperty->GetPropertyValue(Addr);
		// 获取成员变量的元数据,传入key值,这里获取的是元数据中的Category项值
		FString PropertyMetaCategory = StringProperty->GetMetaData(TEXT("Category"));
	}
}

注意:获取类属性的元数据,即获取UPROPERTYUFUNCTION中的元数据信息,同时标记中也存在着元数据的信息。

UCLASS(Min)
class LEARNINGPROJECTV5_API UReflectionTestClass : public UObject
{
    
    
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, Category = "ReflectionTestCategory")
	FString TestParam = "Nello";

	UFUNCTION(BlueprintCallable)
	void TestFuction(const FString& Input);
	
public:
	UReflectionTestClass();
	~UReflectionTestClass();
};

4. 获取类的方法

/** 获取类的方法
 * 注意:类就算没有方法也会自动生成一个可反射的方法
 */
for(TFieldIterator<UFunction> IteratorOfFunction(TestClass); IteratorOfFunction; ++IteratorOfFunction)
{
    
    
	UFunction* Function = *IteratorOfFunction;
	// 获取方法的名称
	FString FunctionName = Function->GetName();
	// 获取方法的元数据标记
	EFunctionFlags FunctionFlags = Function->FunctionFlags;
	// 获取方法的参数,会将返回值也作为参数来输出
	for(TFieldIterator<FProperty> IteratorOfParam(Function); IteratorOfParam; ++IteratorOfParam)
	{
    
    
		FProperty* Param = *IteratorOfParam;
		// 参数的名称
		FString ParamName = Param->GetName();
		// 参数的类型
		FString ParamType = Param->GetCPPType();
		// 参数的标记
		EPropertyFlags ParamFlag = Param->GetPropertyFlags(); 
	}
}

注意:
(1)类会自动生成一个可以被反射的方法。
(2)函数的标记是EFunctionFlags,结果会以位运算的方式进行各位加值,指明该函数的各种属性包含元数据等信息。
(3)参数的标记是EPropertyFlags,结果会以位运算的方式,指明该参数的各种属性,如该参数是否为返回值,以及元数据等信息。

enum EFunctionFlags : uint32
{
    
    
	// Function flags.
	FUNC_None				= 0x00000000,

	FUNC_Final				= 0x00000001,	// Function is final (prebindable, non-overridable function).
	FUNC_RequiredAPI		= 0x00000002,	// Indicates this function is DLL exported/imported.
	FUNC_BlueprintAuthorityOnly= 0x00000004,   // Function will only run if the object has network authority
	FUNC_BlueprintCosmetic	= 0x00000008,   // Function is cosmetic in nature and should not be invoked on dedicated servers
	// FUNC_				= 0x00000010,   // unused.
	// FUNC_				= 0x00000020,   // unused.
	FUNC_Net				= 0x00000040,   // Function is network-replicated.
	FUNC_NetReliable		= 0x00000080,   // Function should be sent reliably on the network.
	FUNC_NetRequest			= 0x00000100,	// Function is sent to a net service
	FUNC_Exec				= 0x00000200,	// Executable from command line.
	FUNC_Native				= 0x00000400,	// Native function.
	FUNC_Event				= 0x00000800,   // Event function.
	FUNC_NetResponse		= 0x00001000,   // Function response from a net service
	FUNC_Static				= 0x00002000,   // Static function.
	FUNC_NetMulticast		= 0x00004000,	// Function is networked multicast Server -> All Clients
	FUNC_UbergraphFunction	= 0x00008000,   // Function is used as the merge 'ubergraph' for a blueprint, only assigned when using the persistent 'ubergraph' frame
	FUNC_MulticastDelegate	= 0x00010000,	// Function is a multi-cast delegate signature (also requires FUNC_Delegate to be set!)
	FUNC_Public				= 0x00020000,	// Function is accessible in all classes (if overridden, parameters must remain unchanged).
	FUNC_Private			= 0x00040000,	// Function is accessible only in the class it is defined in (cannot be overridden, but function name may be reused in subclasses.  IOW: if overridden, parameters don't need to match, and Super.Func() cannot be accessed since it's private.)
	FUNC_Protected			= 0x00080000,	// Function is accessible only in the class it is defined in and subclasses (if overridden, parameters much remain unchanged).
	FUNC_Delegate			= 0x00100000,	// Function is delegate signature (either single-cast or multi-cast, depending on whether FUNC_MulticastDelegate is set.)
	FUNC_NetServer			= 0x00200000,	// Function is executed on servers (set by replication code if passes check)
	FUNC_HasOutParms		= 0x00400000,	// function has out (pass by reference) parameters
	FUNC_HasDefaults		= 0x00800000,	// function has structs that contain defaults
	FUNC_NetClient			= 0x01000000,	// function is executed on clients
	FUNC_DLLImport			= 0x02000000,	// function is imported from a DLL
	FUNC_BlueprintCallable	= 0x04000000,	// function can be called from blueprint code
	FUNC_BlueprintEvent		= 0x08000000,	// function can be overridden/implemented from a blueprint
	FUNC_BlueprintPure		= 0x10000000,	// function can be called from blueprint code, and is also pure (produces no side effects). If you set this, you should set FUNC_BlueprintCallable as well.
	FUNC_EditorOnly			= 0x20000000,	// function can only be called from an editor scrippt.
	FUNC_Const				= 0x40000000,	// function can be called from blueprint code, and only reads state (never writes state)
	FUNC_NetValidate		= 0x80000000,	// function must supply a _Validate implementation

	FUNC_AllFlags		= 0xFFFFFFFF,
};
enum EPropertyFlags : uint64
{
    
    
	CPF_None = 0,

	CPF_Edit							= 0x0000000000000001,	///< Property is user-settable in the editor.
	CPF_ConstParm						= 0x0000000000000002,	///< This is a constant function parameter
	CPF_BlueprintVisible				= 0x0000000000000004,	///< This property can be read by blueprint code
	CPF_ExportObject					= 0x0000000000000008,	///< Object can be exported with actor.
	CPF_BlueprintReadOnly				= 0x0000000000000010,	///< This property cannot be modified by blueprint code
	CPF_Net								= 0x0000000000000020,	///< Property is relevant to network replication.
	CPF_EditFixedSize					= 0x0000000000000040,	///< Indicates that elements of an array can be modified, but its size cannot be changed.
	CPF_Parm							= 0x0000000000000080,	///< Function/When call parameter.
	CPF_OutParm							= 0x0000000000000100,	///< Value is copied out after function call.
	CPF_ZeroConstructor					= 0x0000000000000200,	///< memset is fine for construction
	CPF_ReturnParm						= 0x0000000000000400,	///< Return value.
	CPF_DisableEditOnTemplate			= 0x0000000000000800,	///< Disable editing of this property on an archetype/sub-blueprint
	CPF_NonNullable						= 0x0000000000001000,	///< Object property can never be null
	CPF_Transient   					= 0x0000000000002000,	///< Property is transient: shouldn't be saved or loaded, except for Blueprint CDOs.
	CPF_Config      					= 0x0000000000004000,	///< Property should be loaded/saved as permanent profile.
	//CPF_								= 0x0000000000008000,	///< 
	CPF_DisableEditOnInstance			= 0x0000000000010000,	///< Disable editing on an instance of this class
	CPF_EditConst   					= 0x0000000000020000,	///< Property is uneditable in the editor.
	CPF_GlobalConfig					= 0x0000000000040000,	///< Load config from base class, not subclass.
	CPF_InstancedReference				= 0x0000000000080000,	///< Property is a component references.
	//CPF_								= 0x0000000000100000,	///<
	CPF_DuplicateTransient				= 0x0000000000200000,	///< Property should always be reset to the default value during any type of duplication (copy/paste, binary duplication, etc.)
	//CPF_								= 0x0000000000400000,	///< 
	//CPF_    							= 0x0000000000800000,	///< 
	CPF_SaveGame						= 0x0000000001000000,	///< Property should be serialized for save games, this is only checked for game-specific archives with ArIsSaveGame
	CPF_NoClear							= 0x0000000002000000,	///< Hide clear (and browse) button.
	//CPF_  							= 0x0000000004000000,	///<
	CPF_ReferenceParm					= 0x0000000008000000,	///< Value is passed by reference; CPF_OutParam and CPF_Param should also be set.
	CPF_BlueprintAssignable				= 0x0000000010000000,	///< MC Delegates only.  Property should be exposed for assigning in blueprint code
	CPF_Deprecated  					= 0x0000000020000000,	///< Property is deprecated.  Read it from an archive, but don't save it.
	CPF_IsPlainOldData					= 0x0000000040000000,	///< If this is set, then the property can be memcopied instead of CopyCompleteValue / CopySingleValue
	CPF_RepSkip							= 0x0000000080000000,	///< Not replicated. For non replicated properties in replicated structs 
	CPF_RepNotify						= 0x0000000100000000,	///< Notify actors when a property is replicated
	CPF_Interp							= 0x0000000200000000,	///< interpolatable property for use with cinematics
	CPF_NonTransactional				= 0x0000000400000000,	///< Property isn't transacted
	CPF_EditorOnly						= 0x0000000800000000,	///< Property should only be loaded in the editor
	CPF_NoDestructor					= 0x0000001000000000,	///< No destructor
	//CPF_								= 0x0000002000000000,	///<
	CPF_AutoWeak						= 0x0000004000000000,	///< Only used for weak pointers, means the export type is autoweak
	CPF_ContainsInstancedReference		= 0x0000008000000000,	///< Property contains component references.
	CPF_AssetRegistrySearchable			= 0x0000010000000000,	///< asset instances will add properties with this flag to the asset registry automatically
	CPF_SimpleDisplay					= 0x0000020000000000,	///< The property is visible by default in the editor details view
	CPF_AdvancedDisplay					= 0x0000040000000000,	///< The property is advanced and not visible by default in the editor details view
	CPF_Protected						= 0x0000080000000000,	///< property is protected from the perspective of script
	CPF_BlueprintCallable				= 0x0000100000000000,	///< MC Delegates only.  Property should be exposed for calling in blueprint code
	CPF_BlueprintAuthorityOnly			= 0x0000200000000000,	///< MC Delegates only.  This delegate accepts (only in blueprint) only events with BlueprintAuthorityOnly.
	CPF_TextExportTransient				= 0x0000400000000000,	///< Property shouldn't be exported to text format (e.g. copy/paste)
	CPF_NonPIEDuplicateTransient		= 0x0000800000000000,	///< Property should only be copied in PIE
	CPF_ExposeOnSpawn					= 0x0001000000000000,	///< Property is exposed on spawn
	CPF_PersistentInstance				= 0x0002000000000000,	///< A object referenced by the property is duplicated like a component. (Each actor should have an own instance.)
	CPF_UObjectWrapper					= 0x0004000000000000,	///< Property was parsed as a wrapper class like TSubclassOf<T>, FScriptInterface etc., rather than a USomething*
	CPF_HasGetValueTypeHash				= 0x0008000000000000,	///< This property can generate a meaningful hash value.
	CPF_NativeAccessSpecifierPublic		= 0x0010000000000000,	///< Public native access specifier
	CPF_NativeAccessSpecifierProtected	= 0x0020000000000000,	///< Protected native access specifier
	CPF_NativeAccessSpecifierPrivate	= 0x0040000000000000,	///< Private native access specifier
	CPF_SkipSerialization				= 0x0080000000000000,	///< Property shouldn't be serialized, can still be exported to text
};

5. 获取类的标记

/** 获取类的标记 */
EClassFlags ClassFlags = TestClass->ClassFlags;
// 判断是否存在某一标记
bool ClassHasConfigFlag = TestClass->HasAnyClassFlags(EClassFlags::CLASS_Config);
enum EClassFlags
{
    
    
	/** No Flags */
	CLASS_None				  = 0x00000000u,
	/** Class is abstract and can't be instantiated directly. */
	CLASS_Abstract            = 0x00000001u,
	/** Save object configuration only to Default INIs, never to local INIs. Must be combined with CLASS_Config */
	CLASS_DefaultConfig		  = 0x00000002u,
	/** Load object configuration at construction time. */
	CLASS_Config			  = 0x00000004u,
	/** This object type can't be saved; null it out at save time. */
	CLASS_Transient			  = 0x00000008u,
	/** This object type may not be available in certain context. (i.e. game runtime or in certain configuration). Optional class data is saved separately to other object types. (i.e. might use sidecar files) */
	CLASS_Optional            = 0x00000010u,
	/** */
	CLASS_MatchedSerializers  = 0x00000020u,
	/** Indicates that the config settings for this class will be saved to Project/User*.ini (similar to CLASS_GlobalUserConfig) */
	CLASS_ProjectUserConfig	  = 0x00000040u,
	/** Class is a native class - native interfaces will have CLASS_Native set, but not RF_MarkAsNative */
	CLASS_Native			  = 0x00000080u,
	/** Don't export to C++ header. */
	CLASS_NoExport UE_DEPRECATED(5.1, "CLASS_NoExport should no longer be used. It is no longer being set by engine code.") = 0x00000100u,
	/** Do not allow users to create in the editor. */
	CLASS_NotPlaceable        = 0x00000200u,
	/** Handle object configuration on a per-object basis, rather than per-class. */
	CLASS_PerObjectConfig     = 0x00000400u,
	
	/** Whether SetUpRuntimeReplicationData still needs to be called for this class */
	CLASS_ReplicationDataIsSetUp = 0x00000800u,
	
	/** Class can be constructed from editinline New button. */
	CLASS_EditInlineNew		  = 0x00001000u,
	/** Display properties in the editor without using categories. */
	CLASS_CollapseCategories  = 0x00002000u,
	/** Class is an interface **/
	CLASS_Interface           = 0x00004000u,
	/**  Do not export a constructor for this class, assuming it is in the cpptext **/
	CLASS_CustomConstructor UE_DEPRECATED(5.1, "CLASS_CustomConstructor should no longer be used. It is no longer being set by engine code.") = 0x00008000u,
	/** all properties and functions in this class are const and should be exported as const */
	CLASS_Const			      = 0x00010000u,

	/** Class flag indicating objects of this class need deferred dependency loading */
	CLASS_NeedsDeferredDependencyLoading = 0x00020000u,
	
	/** Indicates that the class was created from blueprint source material */
	CLASS_CompiledFromBlueprint  = 0x00040000u,

	/** Indicates that only the bare minimum bits of this class should be DLL exported/imported */
	CLASS_MinimalAPI	      = 0x00080000u,
	
	/** Indicates this class must be DLL exported/imported (along with all of it's members) */
	CLASS_RequiredAPI	      = 0x00100000u,

	/** Indicates that references to this class default to instanced. Used to be subclasses of UComponent, but now can be any UObject */
	CLASS_DefaultToInstanced  = 0x00200000u,

	/** Indicates that the parent token stream has been merged with ours. */
	CLASS_TokenStreamAssembled  = 0x00400000u,
	/** Class has component properties. */
	CLASS_HasInstancedReference= 0x00800000u,
	/** Don't show this class in the editor class browser or edit inline new menus. */
	CLASS_Hidden			  = 0x01000000u,
	/** Don't save objects of this class when serializing */
	CLASS_Deprecated		  = 0x02000000u,
	/** Class not shown in editor drop down for class selection */
	CLASS_HideDropDown		  = 0x04000000u,
	/** Class settings are saved to <AppData>/..../Blah.ini (as opposed to CLASS_DefaultConfig) */
	CLASS_GlobalUserConfig	  = 0x08000000u,
	/** Class was declared directly in C++ and has no boilerplate generated by UnrealHeaderTool */
	CLASS_Intrinsic			  = 0x10000000u,
	/** Class has already been constructed (maybe in a previous DLL version before hot-reload). */
	CLASS_Constructed		  = 0x20000000u,
	/** Indicates that object configuration will not check against ini base/defaults when serialized */
	CLASS_ConfigDoNotCheckDefaults = 0x40000000u,
	/** Class has been consigned to oblivion as part of a blueprint recompile, and a newer version currently exists. */
	CLASS_NewerVersionExists  = 0x80000000u,
};

总结:
(1)获取标记得到的是位运算之和,标记中不止含有元数据的值,还有各类标明属性的值。
(2)无论是属性标记、函数标记、类标记,都可以通过HasAnyClassFlags进行快速判断是否含有某一标记。

6. 获取类的父子类

/** 获取类的父类的名称 */
UClass* ParentClass = TestObject->GetClass()->GetSuperClass();
FString ParentClassName = ParentClass->GetName();

/** 判断一个类是否是另一个类的子类 */
// 获取静态类
UClass* Class1 = UReflectionTestClass::StaticClass();
UClass* Class2 = UObject::StaticClass();
// 判断 Class1 是否是 Class2 的孩子
if(Class1->IsChildOf(Class2))
{
    
    
	
}

/** 获取特定类的所有子类 */
TArray<UClass*> ChildOfClassResults;
// 第一个参数:传入要查找类的子类的该类;第二个参数:接受返回的数组;第三个参数:是否循环迭代,即是否查找子类的子类
GetDerivedClasses(UReflectionTestClass::StaticClass(), ChildOfClassResults, false);

7. 获取类生成的所有对象

/** 获取特定类生成的所有对象 */
TArray<UObject*> ObjectOfClassResults;
// 第一个参数:传入要查找类的对象的该类;第二个参数:接受返回的数组
GetObjectsOfClass(UReflectionTestClass::StaticClass(), ObjectOfClassResults, false);

8. 根据名称查找相应的类/方法

/** 根据给定字符串查找相应的类 */
// 通过名字查找类的时候不要加名称前缀,A/U等
UClass* ClassByStringName = FindObject<UClass>(ANY_PACKAGE, *FString("ReflectionTestClass"), true);
/** 根据给定字符串查找相应的枚举 */
UEnum* EnumByStringName = FindObject<UEnum>(ANY_PACKAGE, *FString("ReflectionTestEnum"), true);
/** 根据给定字符串查找相应的蓝图类, 注意该蓝图类需要继承自UObject */
UBlueprint* BlueprintByStringName = FindObject<UBlueprint>(ANY_PACKAGE, *FString("BP_ReflectionTestBlueprint"), true);
/** 根据给定字符串查找相应的类的成员函数,EIncludeSuperFlag::ExcludeSuper表示不查找父类 */
UFunction* FindFunctionByStringName = 
	UReflectionTestClass::StaticClass()->FindFunctionByName(TEXT("TestFuction"), EIncludeSuperFlag::ExcludeSuper);

注意:
(1)查找时使用的字符串不用添加U/A等前缀。
(2)查找的类和函数一定具有反射标记

9. 获取枚举中的项名称

/** 查找枚举下的所有项
 * 注意:枚举会自动增加一个项:枚举名_MAX
 */
if(EnumByStringName)
{
    
    
	for(int32 i = 0; i < EnumByStringName->NumEnums(); i++)
	{
    
    
		FString FindEnumItemName = EnumByStringName->GetNameStringByIndex(i);
	}
}

10. 调用类的方法

通过反射使用ProcessEvent()调用类的方法

/** 使用ProcessEvent调用类的成员方法 */
UReflectionTestClass* Object = NewObject<UReflectionTestClass>();
UFunction* Function = 
	UReflectionTestClass::StaticClass()->FindFunctionByName(TEXT("TestFuction"), EIncludeSuperFlag::ExcludeSuper);
if(Function)
{
    
    
	// 1. 给所有方法参数分配空间并初始化为0
	uint8* FunctionParamMemory = static_cast<uint8*>(FMemory_Alloca(Function->ParmsSize));
	FMemory::Memzero(FunctionParamMemory, Function->ParmsSize);
	// 2. 给所有方法参数赋值
	for(TFieldIterator<FProperty> IteratorOfParam(Function); IteratorOfParam; ++IteratorOfParam)
	{
    
    
		FProperty* Param = *IteratorOfParam;
		FString FunctionParamName = Param->GetName();
		// 通过参数名找到该传入参数
		if(FunctionParamName == FString("Input"))
		{
    
    
			// 设置传入参数的值
			*Param->ContainerPtrToValuePtr<FString>(FunctionParamMemory) = "One";
		}
	}
	// 3. 通过ProcessEvent调用方法
	Object->ProcessEvent(Function, FunctionParamMemory);
}

通过反射使用Invoke()调用类的方法

/** 使用Invoke调用类的成员方法 */
UReflectionTestClass* Object = NewObject<UReflectionTestClass>();
UFunction* Function = 
	UReflectionTestClass::StaticClass()->FindFunctionByName(TEXT("TestFuction"), EIncludeSuperFlag::ExcludeSuper);
if(Function)
{
    
    
	// 1. 给所有方法参数分配空间并初始化为0
	uint8* FunctionParamMemory = static_cast<uint8*>(FMemory_Alloca(Function->ParmsSize));
	FMemory::Memzero(FunctionParamMemory, Function->ParmsSize);
	// 2. 创建一个Frame
	FFrame Frame(nullptr, Function, &FunctionParamMemory);
	// 3. 调用Invoke,第一个参数:调用方法的对象;第二个参数:Frame;第三个参数:为返回值,没有返回值就设为nullptr
	Function->Invoke(Object, Frame, nullptr);
}

11. 遍历所有类

/** 遍历所有的类 */
for(TObjectIterator<UClass> ClassIterator; ClassIterator; ++ClassIterator)
{
    
    
	FString ClassName = ClassIterator->GetName();
}

猜你喜欢

转载自blog.csdn.net/qq_45617648/article/details/131786513
今日推荐