diff --git a/source/Handlebars/MemberAccessors/ReflectionMemberAccessor.cs b/source/Handlebars/MemberAccessors/ReflectionMemberAccessor.cs
index 1542a6a4..87b69fdd 100644
--- a/source/Handlebars/MemberAccessors/ReflectionMemberAccessor.cs
+++ b/source/Handlebars/MemberAccessors/ReflectionMemberAccessor.cs
@@ -29,6 +29,34 @@ public bool TryGetValue(object instance, ChainSegment memberName, out object? va
var instanceType = instance.GetType();
if (TryGetValueImpl(instance, instanceType, memberName, out value)) return true;
+ return TryGetValueByAlias(instance, instanceType, memberName, out value);
+ }
+
+ ///
+ /// Creates an accessor pre-bound to so that per-access
+ /// type-descriptor lookup is skipped when the instance type matches.
+ ///
+ internal IMemberAccessor CreateBoundAccessor(Type type) => new BoundMemberAccessor(this, type, GetTypeDescriptor(type));
+
+ private RawObjectTypeDescriptor GetTypeDescriptor(Type instanceType)
+ {
+ if (!_descriptors.TryGetValue(instanceType, out var deferredValue))
+ {
+ deferredValue = _descriptors.GetOrAdd(instanceType, DescriptorsValueFactory);
+ }
+
+ return deferredValue.Value;
+ }
+
+ private bool TryGetValueImpl(object instance, Type instanceType, ChainSegment memberName, out object? value)
+ {
+ var accessor = GetTypeDescriptor(instanceType).GetOrCreateAccessor(memberName);
+ value = accessor?.Invoke(instance);
+ return accessor != null;
+ }
+
+ private bool TryGetValueByAlias(object instance, Type instanceType, ChainSegment memberName, out object? value)
+ {
for (var index = 0; index < _aliasProviders.Count; index++)
{
if (_aliasProviders[index].TryGetMemberByAlias(instance, instanceType, memberName, out value))
@@ -39,16 +67,35 @@ public bool TryGetValue(object instance, ChainSegment memberName, out object? va
return false;
}
- private bool TryGetValueImpl(object instance, Type instanceType, ChainSegment memberName, out object? value)
+ private sealed class BoundMemberAccessor : IMemberAccessor
{
- if (!_descriptors.TryGetValue(instanceType, out var deferredValue))
+ private readonly ReflectionMemberAccessor _owner;
+ private readonly Type _type;
+ private readonly RawObjectTypeDescriptor _typeDescriptor;
+
+ public BoundMemberAccessor(ReflectionMemberAccessor owner, Type type, RawObjectTypeDescriptor typeDescriptor)
{
- deferredValue = _descriptors.GetOrAdd(instanceType, DescriptorsValueFactory);
+ _owner = owner;
+ _type = type;
+ _typeDescriptor = typeDescriptor;
}
- var accessor = deferredValue.Value.GetOrCreateAccessor(memberName);
- value = accessor?.Invoke(instance);
- return accessor != null;
+ public bool TryGetValue(object instance, ChainSegment memberName, out object? value)
+ {
+ if (!ReferenceEquals(instance.GetType(), _type))
+ {
+ return _owner.TryGetValue(instance, memberName, out value);
+ }
+
+ var accessor = _typeDescriptor.GetOrCreateAccessor(memberName);
+ if (accessor != null)
+ {
+ value = accessor.Invoke(instance);
+ return true;
+ }
+
+ return _owner.TryGetValueByAlias(instance, _type, memberName, out value);
+ }
}
private sealed class RawObjectTypeDescriptor
@@ -113,6 +160,15 @@ public RawObjectTypeDescriptor(Type type)
{
// Value types must be passed by reference
var @delegate = (ValueTypeGetterDelegate)property.GetMethod!.CreateDelegate(typeof(ValueTypeGetterDelegate));
+ if (@delegate is ValueTypeGetterDelegate boolValueTypeGetter)
+ {
+ return o =>
+ {
+ var to = (T)o;
+ return boolValueTypeGetter(ref to) ? BoxedValues.True : BoxedValues.False;
+ };
+ }
+
return o =>
{
var to = (T)o;
@@ -122,9 +178,14 @@ public RawObjectTypeDescriptor(Type type)
else
{
var @delegate = (Func) property.GetMethod!.CreateDelegate(typeof(Func));
+ if (@delegate is Func boolGetter)
+ {
+ return o => boolGetter((T) o) ? BoxedValues.True : BoxedValues.False;
+ }
+
return o => (object?) @delegate((T) o);
}
}
}
}
-}
\ No newline at end of file
+}
diff --git a/source/Handlebars/ObjectDescriptors/ObjectDescriptorProvider.cs b/source/Handlebars/ObjectDescriptors/ObjectDescriptorProvider.cs
index 1304d1aa..4afa9cea 100644
--- a/source/Handlebars/ObjectDescriptors/ObjectDescriptorProvider.cs
+++ b/source/Handlebars/ObjectDescriptors/ObjectDescriptorProvider.cs
@@ -25,7 +25,7 @@ public bool TryGetDescriptor(Type type, out ObjectDescriptor value)
{
value = new ObjectDescriptor(
type,
- _reflectionMemberAccessor,
+ _reflectionMemberAccessor.CreateBoundAccessor(type),
GetProperties,
self => new ObjectIterator(self),
dependencies: _membersCache