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