# 属性访问者

> Understand the role of property visitors and the performance considerations when using them.

属性访问者是在 Properties API 的基础上构建的算法。

## 概念##create-a-property-visitor-to-get-properties

可使用访问者向类型添加功能，而无需直接修改。可以创建高度通用的访问者来控制算法本身和访问过程。这与访问者模式的典型实现方式不同，后者的访问过程通常发生在一个事先已知的类型结构上。它支持[序列化](https://docs.unity3d.com/Packages/com.unity.serialization@3.1/manual/index.html)、类似于检视视图的 UI 生成等功能。

以下是访问者的基本模式。它发生在属性包和属性伴生对象上。

1. 一个类型的实例**接受**一个访问者。
2. 该访问者**访问**该实例的属性包。
3. 属性包可以循环访问其属性并**接受**访问者。

## 创建属性访问者以获取属性##performance-considerations

可以使用以下方法来创建访问者以获取属性：

* 使用 [`Unity.Properties.PropertyVisitor`](/engine/6000.0/script-reference/unity-properties/property-visitor.md) 基类。有关示例，请参阅[使用 `PropertyVisitor` 创建属性访问者](/engine/6000.0/manual/scripting/object-oriented-development/properties/property-visitors-propertyvisitor.md)。
* 实现 [`IPropertyBagVisitor`](/engine/6000.0/script-reference/unity-properties/i-property-bag-visitor.md) 和 [`IPropertyVisitor`](/engine/6000.0/script-reference/unity-properties/i-property-visitor.md) 接口。有关示例，请参阅[使用低级 API 创建属性访问者](/engine/6000.0/manual/scripting/object-oriented-development/properties/property-visitors-low-level-api.md)。

第一种方法是最简单的入门方法。但是，为了更广泛地自定义属性包和属性的访问行为，请使用第二种方法，这种方法具有更大的灵活性和改进性能的潜力。

以下示例使用 [`PropertyVisitor`](/engine/6000.0/script-reference/unity-properties/property-visitor.md) 类来创建一个简单的访问者，该访问者可获取特定类型并带有特定属性标签的属性：

```lang-csharp
public class BindableAttribute
    : Attribute
{
}

public class GatherBindablePropertiesVisitor
    : PropertyVisitor
{
    public List<PropertyPath> BindableProperties { get; set; }

    protected override void VisitProperty<TContainer, TValue>(Property<TContainer, TValue> property, ref TContainer container, ref TValue value)
    {
        if (property.HasAttribute<BindableAttribute>())
            BindableProperties.Add(PropertyPath.AppendProperty(default, property));
    }
}
```

以下是使用 `IPropertyBagVisitor` 接口创建访问者的对应示例：

```lang-csharp
public class BindableAttribute
    : Attribute
{
}

public class GatherBindablePropertiesVisitor
    : IPropertyBagVisitor
{
    public List<PropertyPath> BindableProperties { get; set; }

    void IPropertyBagVisitor.Visit<TContainer>(IPropertyBag<TContainer> propertyBag, ref TContainer container)
    {
        // Loop through the properties of the container object.
        foreach (var property in propertyBag.GetProperties(ref container))
        {
            if (property.HasAttribute<BindableAttribute>())
                BindableProperties.Add(PropertyPath.AppendProperty(default, property));
        }
    }
}
```

低级访问者的性能更佳，因为它不需要循环访问属性包的所有属性并提取它们的值。还可以使用低级访问者访问不属于属性包的属性。

## 性能注意事项##additional-resources

属性包、属性和访问者都使用通用类型实现，以便可以保持尽可能强的类型，并在许多情况下避免在访问期间发生装箱分配。使用通用类型的利弊在于 JIT 编译器将在第一次调用指定方法时为其生成 IL。这可能导致第一次接受对象的访问者时执行速度变慢。

## 其他资源

* [属性包](/engine/6000.0/manual/scripting/object-oriented-development/properties/property-bags.md)
* [属性路径](/engine/6000.0/manual/scripting/object-oriented-development/properties/property-paths.md)
* [使用 `PropertyVisitor` 创建属性访问者](/engine/6000.0/manual/scripting/object-oriented-development/properties/property-visitors-propertyvisitor.md)
* [使用低级 API 创建属性访问者](/engine/6000.0/manual/scripting/object-oriented-development/properties/property-visitors-low-level-api.md)
