Property visitors are algorithms built on top of the Properties API.
You can use visitors to add functionality to types without the need for direct modifications. You can create highly generic visitors to control both the algorithm itself and the visitation process. This differs from the classic implementation of the visitor pattern, where the visitation typically occurs on a known ahead-of-time type structure. It enables features like serialization, inspector-like UI generation, among others.
The following is the basic pattern of visitors. It takes place on the property bags and property companion objects.
You can use the following approaches to create your visitors to get properties:
Unity.Properties.PropertyVisitor
base class. Refer to Use PropertyVisitor
to create a property visitor for an example.IPropertyBagVisitor
and IPropertyVisitor
interfaces. Refer to Use low-level APIs to create a property visitor for an example.The first approach is the easiest way to get started. However, for more extensive customization of the visitation behavior for both the property bags and the properties, use the second approach, which offers greater flexibility and the potential for performance improvements.
The following example uses the PropertyVisitor
class to create a simple visitor that gets the properties of a given type that are tagged with a certain attribute:
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));
}
}
The following is the equivalent example that uses the IPropertyBagVisitor
interface to create the visitor:
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));
}
}
}
The low-level visitor is more performant because it doesn’t need to loop through all the properties of the property bag and extract their value. You can also use low-level visitors to visit properties that aren’t part of a property bag.
Property bags, properties and visitors are all implemented using generic types so that we can remain as strongly-typed as possible and, in many cases, avoid boxing allocations during visitation. The trade-off of using generic types is that the JIT compiler will generate the IL for a given method the first time it is called. This can result in a slower execution the first time a visitor is accepted on an object.