属性包是针对给定 .Net 对象类型的一组属性,您可以使用它们来访问和设置该类型对象的实例数据。
针对给定类型的属性包是一个伴随对象,它支持基于该类型实例的高效数据遍历算法。默认情况下,Unity 使用反射来为类型生成属性包。这种反射方法提供便利,并且仅在尚未注册属性包时才按类型延迟执行一次。
为了提高性能,您可以通过用 [Unity.Properties.GeneratePropertyBag]
标记类型来选择代码生成。此外,要激活代码生成,您必须用 [assembly: Unity.Properties.GeneratePropertyBagsForAssembly]
标记程序集。代码生成的属性包在加载域时会自动注册。
在反射和代码生成这两种情况下,属性包都会为以下内容生成属性:
[SerializeField]
、[SerializeReference]
或 [CreateProperty]
标记的私有或内部字段[Unity.Properties.CreateProperty]
标记的公有、私有或内部属性属性包不会为用 [DontCreateProperty]
标记的公有、私有或内部字段生成属性。
如果字段是只读的,或者属性只有获取器,则生成的属性是只读的。
您还可以使用 [Unity.Properties.CreateProperty(ReadOnly = true)]
使生成的属性变为只读。
使用序列化属性在属性包中创建属性以获得便利并不总是最佳方法。Unity 的序列化系统只能对字段和自动属性起作用,这使得有效地实现验证或传播更改变得很困难。
以下示例将 Unity 序列化系统与 Unity Properties 系统相结合。
using UnityEngine;
using Unity.Properties;
public class MyBehaviour : MonoBehaviour
{
// Serializations go through the field, but we don't want to create a property for it.
[SerializeField, DontCreateProperty]
private int m_Value;
// For the property bag, use the property instead of the field. This ensures that
// the value stays within the appropriate bounds.
[CreateProperty]
public int value
{
get => m_Value;
set => m_Value = value;
}
// This is a similar example, but for an auto-property.
[field: SerializeField, DontCreateProperty]
[CreateProperty]
public float floatValue { get; set; }
}
与 Unity 序列化系统不同,属性包中的属性不符合带有 [SerializeField]
的值类型的条件。相反,结构类型被识别为值类型,而类类型被识别为引用类型。
在 Unity 序列化中,虽然支持多态性,但您必须使用 [SerializeReference]
属性来明确选择加入。否则,实例将被序列化为值类型。值得注意的是,UnityEngine.Object
类型是此规则的例外,因为它们会自动被序列化为引用类型。
Unity Properties 使用 .Net 反射来创建强类型化的属性包和属性,这会在您第一次为给定容器类型请求属性包时引入性能开销。
当您通过反射为字段成员创建属性时,这些属性可能会在 IL2CPP一种 Unity 开发的脚本后端,可以在构建某些平台的项目时用作 Mono 的替代方案。更多信息
查看 词汇表 构建中分配垃圾。这种分配是由于直接使用 System.Reflection.FieldInfo
造成的,这会导致不可避免的装箱。
要避免反射,您可以在编译期间代码生成属性包。但是,请注意,这种优化可能会导致更长的编译时间。要为程序集启用代码生成,请用 [Unity.Properties.GeneratePropertyBagsForAssemblyAttribute]
标记程序集,并用 [Unity.Properties.GeneratePropertyBagAttribute]
标记单个类型。要使属性包能够访问内部和私有字段和属性,请将类型设为 partial
。