Browse Source

performance optimization of

- AvaloniaObject.GetValue
- AvaloniaPropertyRegistry.IsRegistered
- AvaloniaProperty.GetMetadata
pull/558/head
donandren 10 years ago
parent
commit
7c5545f108
  1. 45
      src/Avalonia.Base/AvaloniaObject.cs
  2. 54
      src/Avalonia.Base/AvaloniaProperty.cs
  3. 44
      src/Avalonia.Base/AvaloniaPropertyRegistry.cs

45
src/Avalonia.Base/AvaloniaObject.cs

@ -261,25 +261,12 @@ namespace Avalonia
} }
else else
{ {
object result = AvaloniaProperty.UnsetValue;
PriorityValue value;
if (!AvaloniaPropertyRegistry.Instance.IsRegistered(this, property)) if (!AvaloniaPropertyRegistry.Instance.IsRegistered(this, property))
{ {
ThrowNotRegistered(property); ThrowNotRegistered(property);
} }
if (_values.TryGetValue(property, out value)) return GetValueInternal(property);
{
result = value.Value;
}
if (result == AvaloniaProperty.UnsetValue)
{
result = GetDefaultValue(property);
}
return result;
} }
} }
@ -767,7 +754,7 @@ namespace Avalonia
{ {
if (property.Inherits && _inheritanceParent != null) if (property.Inherits && _inheritanceParent != null)
{ {
return _inheritanceParent.GetValue(property); return (_inheritanceParent as AvaloniaObject).GetValueInternal(property);
} }
else else
{ {
@ -775,6 +762,32 @@ namespace Avalonia
} }
} }
/// <summary>
/// Gets a <see cref="AvaloniaProperty"/> value
/// without check for registered as this can slow getting the value
/// this method is intended for internal usage in AvaloniaObject only
/// it's called only after check the property is registered
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The value.</returns>
private object GetValueInternal(AvaloniaProperty property)
{
object result = AvaloniaProperty.UnsetValue;
PriorityValue value;
if (_values.TryGetValue(property, out value))
{
result = value.Value;
}
if (result == AvaloniaProperty.UnsetValue)
{
result = GetDefaultValue(property);
}
return result;
}
/// <summary> /// <summary>
/// Given a <see cref="AvaloniaProperty"/> returns a registered avalonia property that is /// Given a <see cref="AvaloniaProperty"/> returns a registered avalonia property that is
/// equal or throws if not found. /// equal or throws if not found.
@ -849,4 +862,4 @@ namespace Avalonia
throw new ArgumentException($"Property '{p.Name} not registered on '{this.GetType()}"); throw new ArgumentException($"Property '{p.Name} not registered on '{this.GetType()}");
} }
} }
} }

54
src/Avalonia.Base/AvaloniaProperty.cs

@ -26,6 +26,7 @@ namespace Avalonia
private readonly Subject<AvaloniaPropertyChangedEventArgs> _changed; private readonly Subject<AvaloniaPropertyChangedEventArgs> _changed;
private readonly PropertyMetadata _defaultMetadata; private readonly PropertyMetadata _defaultMetadata;
private readonly Dictionary<Type, PropertyMetadata> _metadata; private readonly Dictionary<Type, PropertyMetadata> _metadata;
private readonly Dictionary<Type, PropertyMetadata> _metadataCache = new Dictionary<Type, PropertyMetadata>();
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AvaloniaProperty"/> class. /// Initializes a new instance of the <see cref="AvaloniaProperty"/> class.
@ -73,8 +74,8 @@ namespace Avalonia
/// <param name="ownerType">The new owner type.</param> /// <param name="ownerType">The new owner type.</param>
/// <param name="metadata">Optional overridden metadata.</param> /// <param name="metadata">Optional overridden metadata.</param>
protected AvaloniaProperty( protected AvaloniaProperty(
AvaloniaProperty source, AvaloniaProperty source,
Type ownerType, Type ownerType,
PropertyMetadata metadata) PropertyMetadata metadata)
{ {
Contract.Requires<ArgumentNullException>(source != null); Contract.Requires<ArgumentNullException>(source != null);
@ -163,11 +164,11 @@ namespace Avalonia
/// object. /// object.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// When a property changes, change notifications are sent to all property subscribers; /// When a property changes, change notifications are sent to all property subscribers;
/// for example via the <see cref="AvaloniaProperty.Changed"/> observable and and the /// for example via the <see cref="AvaloniaProperty.Changed"/> observable and and the
/// <see cref="AvaloniaObject.PropertyChanged"/> event. If this callback is set for a property, /// <see cref="AvaloniaObject.PropertyChanged"/> event. If this callback is set for a property,
/// then it will be called before and after these notifications take place. The bool argument /// then it will be called before and after these notifications take place. The bool argument
/// will be true before the property change notifications are sent and false afterwards. This /// will be true before the property change notifications are sent and false afterwards. This
/// callback is intended to support Control.IsDataContextChanging. /// callback is intended to support Control.IsDataContextChanging.
/// </remarks> /// </remarks>
public Action<IAvaloniaObject, bool> Notifying { get; } public Action<IAvaloniaObject, bool> Notifying { get; }
@ -273,8 +274,8 @@ namespace Avalonia
defaultBindingMode: defaultBindingMode); defaultBindingMode: defaultBindingMode);
var result = new StyledProperty<TValue>( var result = new StyledProperty<TValue>(
name, name,
typeof(TOwner), typeof(TOwner),
metadata, metadata,
inherits, inherits,
notifying); notifying);
@ -423,24 +424,9 @@ namespace Avalonia
/// </returns> /// </returns>
public PropertyMetadata GetMetadata<T>() where T : IAvaloniaObject public PropertyMetadata GetMetadata<T>() where T : IAvaloniaObject
{ {
var type = typeof(T); return GetMetadata(typeof(T));
while (type != null)
{
PropertyMetadata result;
if (_metadata.TryGetValue(type, out result))
{
return result;
}
type = type.GetTypeInfo().BaseType;
}
return _defaultMetadata;
} }
/// <summary> /// <summary>
/// Gets the property metadata for the specified type. /// Gets the property metadata for the specified type.
/// </summary> /// </summary>
@ -448,22 +434,33 @@ namespace Avalonia
/// <returns> /// <returns>
/// The property metadata. /// The property metadata.
/// </returns> /// </returns>
///
public PropertyMetadata GetMetadata(Type type) public PropertyMetadata GetMetadata(Type type)
{ {
Contract.Requires<ArgumentNullException>(type != null); Contract.Requires<ArgumentNullException>(type != null);
while (type != null) PropertyMetadata result;
Type currentType = type;
if (_metadataCache.TryGetValue(type, out result))
{ {
PropertyMetadata result; return result;
}
if (_metadata.TryGetValue(type, out result)) while (currentType != null)
{
if (_metadata.TryGetValue(currentType, out result))
{ {
_metadataCache[type] = result;
return result; return result;
} }
type = type.GetTypeInfo().BaseType; currentType = currentType.GetTypeInfo().BaseType;
} }
_metadataCache[type] = _defaultMetadata;
return _defaultMetadata; return _defaultMetadata;
} }
@ -523,6 +520,7 @@ namespace Avalonia
var baseMetadata = GetMetadata(type); var baseMetadata = GetMetadata(type);
metadata.Merge(baseMetadata, this); metadata.Merge(baseMetadata, this);
_metadata.Add(type, metadata); _metadata.Add(type, metadata);
_metadataCache.Clear();
} }
[DebuggerHidden] [DebuggerHidden]
@ -551,4 +549,4 @@ namespace Avalonia
public override string ToString() => "(unset)"; public override string ToString() => "(unset)";
} }
} }
} }

44
src/Avalonia.Base/AvaloniaPropertyRegistry.cs

@ -20,6 +20,12 @@ namespace Avalonia
private readonly Dictionary<Type, Dictionary<int, AvaloniaProperty>> _registered = private readonly Dictionary<Type, Dictionary<int, AvaloniaProperty>> _registered =
new Dictionary<Type, Dictionary<int, AvaloniaProperty>>(); new Dictionary<Type, Dictionary<int, AvaloniaProperty>>();
/// <summary>
/// The registered properties by type cached values to increase performance.
/// </summary>
private readonly Dictionary<Type, Dictionary<int, AvaloniaProperty>> _registeredCache =
new Dictionary<Type, Dictionary<int, AvaloniaProperty>>();
/// <summary> /// <summary>
/// The registered attached properties by owner type. /// The registered attached properties by owner type.
/// </summary> /// </summary>
@ -103,21 +109,38 @@ namespace Avalonia
/// </remarks> /// </remarks>
public AvaloniaProperty FindRegistered(Type type, AvaloniaProperty property) public AvaloniaProperty FindRegistered(Type type, AvaloniaProperty property)
{ {
while (type != null) Type currentType = type;
Dictionary<int, AvaloniaProperty> cache;
AvaloniaProperty result;
if (_registeredCache.TryGetValue(type, out cache))
{
if (cache.TryGetValue(property.Id, out result))
{
return result;
}
}
while (currentType != null)
{ {
Dictionary<int, AvaloniaProperty> inner; Dictionary<int, AvaloniaProperty> inner;
if (_registered.TryGetValue(type, out inner)) if (_registered.TryGetValue(currentType, out inner))
{ {
AvaloniaProperty result;
if (inner.TryGetValue(property.Id, out result)) if (inner.TryGetValue(property.Id, out result))
{ {
if (cache == null)
{
_registeredCache[type] = cache = new Dictionary<int, AvaloniaProperty>();
}
cache[property.Id] = result;
return result; return result;
} }
} }
type = type.GetTypeInfo().BaseType; currentType = currentType.GetTypeInfo().BaseType;
} }
return null; return null;
@ -130,7 +153,7 @@ namespace Avalonia
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
/// <returns>The registered property or null if not found.</returns> /// <returns>The registered property or null if not found.</returns>
/// <remarks> /// <remarks>
/// Calling AddOwner on a AvaloniaProperty creates a new AvaloniaProperty that is a /// Calling AddOwner on a AvaloniaProperty creates a new AvaloniaProperty that is a
/// different object but is equal according to <see cref="object.Equals(object)"/>. /// different object but is equal according to <see cref="object.Equals(object)"/>.
/// </remarks> /// </remarks>
public AvaloniaProperty FindRegistered(object o, AvaloniaProperty property) public AvaloniaProperty FindRegistered(object o, AvaloniaProperty property)
@ -143,7 +166,7 @@ namespace Avalonia
/// </summary> /// </summary>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <param name="name"> /// <param name="name">
/// The property name. If an attached property it should be in the form /// The property name. If an attached property it should be in the form
/// "OwnerType.PropertyName". /// "OwnerType.PropertyName".
/// </param> /// </param>
/// <returns> /// <returns>
@ -183,13 +206,12 @@ namespace Avalonia
return results.FirstOrDefault(x => x.Name == propertyName); return results.FirstOrDefault(x => x.Name == propertyName);
} }
/// <summary> /// <summary>
/// Finds a registered property on an object by name. /// Finds a registered property on an object by name.
/// </summary> /// </summary>
/// <param name="o">The object.</param> /// <param name="o">The object.</param>
/// <param name="name"> /// <param name="name">
/// The property name. If an attached property it should be in the form /// The property name. If an attached property it should be in the form
/// "OwnerType.PropertyName". /// "OwnerType.PropertyName".
/// </param> /// </param>
/// <returns> /// <returns>
@ -276,6 +298,8 @@ namespace Avalonia
inner.Add(property.Id, property); inner.Add(property.Id, property);
} }
} }
_registeredCache.Clear();
} }
} }
} }
Loading…
Cancel
Save