Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 68 additions & 7 deletions source/Handlebars/MemberAccessors/ReflectionMemberAccessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/// <summary>
/// Creates an accessor pre-bound to <paramref name="type"/> so that per-access
/// type-descriptor lookup is skipped when the instance type matches.
/// </summary>
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))
Expand All @@ -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
Expand Down Expand Up @@ -113,6 +160,15 @@ public RawObjectTypeDescriptor(Type type)
{
// Value types must be passed by reference
var @delegate = (ValueTypeGetterDelegate<T, TValue>)property.GetMethod!.CreateDelegate(typeof(ValueTypeGetterDelegate<T, TValue>));
if (@delegate is ValueTypeGetterDelegate<T, bool> boolValueTypeGetter)
{
return o =>
{
var to = (T)o;
return boolValueTypeGetter(ref to) ? BoxedValues.True : BoxedValues.False;
};
}

return o =>
{
var to = (T)o;
Expand All @@ -122,9 +178,14 @@ public RawObjectTypeDescriptor(Type type)
else
{
var @delegate = (Func<T, TValue>) property.GetMethod!.CreateDelegate(typeof(Func<T, TValue>));
if (@delegate is Func<T, bool> boolGetter)
{
return o => boolGetter((T) o) ? BoxedValues.True : BoxedValues.False;
}

return o => (object?) @delegate((T) o);
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading