From ec14c655edc3c027f3e103dfb65efb7363fcfd71 Mon Sep 17 00:00:00 2001 From: ChebanovDD Date: 2023年7月16日 18:49:35 +0800 Subject: [PATCH 01/10] Resolve #48. Add PropertyCastWrappers. --- .../BindingContextObjectProvider.cs | 28 +++++++++++ .../Interfaces/IObjectProvider.cs | 4 +- .../ObjectHandlers/ObjectWrapperHandler.cs | 49 +++++++++++++++++++ .../PropertyCastWrapper.TSource.TValue.cs | 19 +++++++ .../PropertyWrapper.TSource.TValue.cs | 23 ++++++--- ...dOnlyPropertyCastWrapper.TSource.TValue.cs | 14 ++++++ .../ReadOnlyPropertyWrapper.TSource.TValue.cs | 12 +++-- .../Core/BindingContextObjectProvider.cs | 28 +++++++++++ .../Core/Interfaces/IObjectProvider.cs | 4 +- .../ObjectHandlers/ObjectWrapperHandler.cs | 49 +++++++++++++++++++ .../PropertyCastWrapper.TSource.TValue.cs | 19 +++++++ ...PropertyCastWrapper.TSource.TValue.cs.meta | 11 +++++ .../PropertyWrapper.TSource.TValue.cs | 23 ++++++--- ...dOnlyPropertyCastWrapper.TSource.TValue.cs | 14 ++++++ ...PropertyCastWrapper.TSource.TValue.cs.meta | 11 +++++ .../ReadOnlyPropertyWrapper.TSource.TValue.cs | 12 +++-- 16 files changed, 298 insertions(+), 22 deletions(-) create mode 100644 src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/PropertyCastWrapper.TSource.TValue.cs create mode 100644 src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/ReadOnlyPropertyCastWrapper.TSource.TValue.cs create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyCastWrapper.TSource.TValue.cs create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyCastWrapper.TSource.TValue.cs.meta create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyCastWrapper.TSource.TValue.cs create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyCastWrapper.TSource.TValue.cs.meta diff --git a/src/UnityMvvmToolkit.Core/BindingContextObjectProvider.cs b/src/UnityMvvmToolkit.Core/BindingContextObjectProvider.cs index f936988..ced8566 100644 --- a/src/UnityMvvmToolkit.Core/BindingContextObjectProvider.cs +++ b/src/UnityMvvmToolkit.Core/BindingContextObjectProvider.cs @@ -86,6 +86,14 @@ public IProperty RentProperty(IBindingContext context, P return GetProperty, TValueType>(context, bindingData); } + public IProperty RentPropertyAs(IBindingContext context, + PropertyBindingData bindingData) + { + EnsureBindingDataValid(bindingData); + + return GetPropertyAs, TValueType>(context, bindingData); + } + public void ReturnProperty(IProperty property) { ReturnBaseProperty(property); @@ -99,6 +107,14 @@ public IReadOnlyProperty RentReadOnlyProperty(IBindingCo return GetProperty, TValueType>(context, bindingData); } + public IReadOnlyProperty RentReadOnlyPropertyAs(IBindingContext context, + PropertyBindingData bindingData) + { + EnsureBindingDataValid(bindingData); + + return GetPropertyAs, TValueType>(context, bindingData); + } + public void ReturnReadOnlyProperty(IReadOnlyProperty property) { ReturnBaseProperty(property); @@ -167,6 +183,18 @@ private TProperty GetProperty(IBindingContext context, Bi return _objectWrapperHandler.GetProperty(context, bindingData, memberInfo); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private TProperty GetPropertyAs(IBindingContext context, BindingData bindingData) + where TProperty : IBaseProperty + { + if (TryGetContextMemberInfo(context.GetType(), bindingData.PropertyName, out var memberInfo) == false) + { + throw new InvalidOperationException($"Property '{bindingData.PropertyName}' not found."); + } + + return _objectWrapperHandler.GetPropertyAs(context, memberInfo); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool TryGetContextMemberInfo(Type contextType, string memberName, out MemberInfo memberInfo) { diff --git a/src/UnityMvvmToolkit.Core/Interfaces/IObjectProvider.cs b/src/UnityMvvmToolkit.Core/Interfaces/IObjectProvider.cs index 51fb5ef..aec5c3b 100644 --- a/src/UnityMvvmToolkit.Core/Interfaces/IObjectProvider.cs +++ b/src/UnityMvvmToolkit.Core/Interfaces/IObjectProvider.cs @@ -15,11 +15,13 @@ IObjectProvider WarmupValueConverter(int capacity, WarmupType warmupType = Wa where T : IValueConverter; IProperty RentProperty(IBindingContext context, PropertyBindingData bindingData); + IProperty RentPropertyAs(IBindingContext context, PropertyBindingData bindingData); void ReturnProperty(IProperty property); IReadOnlyProperty RentReadOnlyProperty(IBindingContext context, PropertyBindingData bindingData); - + IReadOnlyProperty RentReadOnlyPropertyAs(IBindingContext context, + PropertyBindingData bindingData); void ReturnReadOnlyProperty(IReadOnlyProperty property); TCommand GetCommand(IBindingContext context, string propertyName) where TCommand : IBaseCommand; diff --git a/src/UnityMvvmToolkit.Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs b/src/UnityMvvmToolkit.Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs index e8b9d96..543643e 100644 --- a/src/UnityMvvmToolkit.Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs +++ b/src/UnityMvvmToolkit.Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; +using UnityMvvmToolkit.Core.Converters.PropertyValueConverters; using UnityMvvmToolkit.Core.Enums; using UnityMvvmToolkit.Core.Interfaces; using UnityMvvmToolkit.Core.Internal.Extensions; @@ -50,6 +51,54 @@ public void CreateValueConverterInstances(int capacity, WarmupType warmupType } } + public TProperty GetPropertyAs(IBindingContext context, MemberInfo memberInfo) + where TProperty : IBaseProperty + { + var property = GetMemberValue(context, memberInfo, out var propertyType); + + var targetType = typeof(TValueType); + var sourceType = propertyType.GenericTypeArguments[0]; + + if (targetType == sourceType) + { + return (TProperty) property; + } + + if (targetType.IsValueType || sourceType.IsValueType) + { + throw new InvalidOperationException( + $"{nameof(GetPropertyAs)} is not supported for value types. Use {typeof(PropertyValueConverter<,>).Name} instead."); + } + + if (targetType.IsAssignableFrom(sourceType) == false) + { + throw new InvalidCastException($"Can not cast the '{sourceType}' to the '{targetType}'."); + } + + var converterId = HashCodeHelper.GetPropertyWrapperConverterId(targetType, sourceType); + + if (_wrappersByConverter.TryGetValue(converterId, out var propertyWrappers)) + { + if (propertyWrappers.Count> 0) + { + return (TProperty) propertyWrappers + .Dequeue() + .AsPropertyWrapper() + .SetProperty(property); + } + } + else + { + _wrappersByConverter.Add(converterId, new Queue()); + } + + var wrapperType = property is IProperty + ? typeof(PropertyCastWrapper<,>).MakeGenericType(sourceType, targetType) + : typeof(ReadOnlyPropertyCastWrapper<,>).MakeGenericType(sourceType, targetType); + + return (TProperty) ObjectWrapperHelper.CreatePropertyWrapper(wrapperType, default, converterId, property); + } + public TProperty GetProperty(IBindingContext context, BindingData bindingData, MemberInfo memberInfo) where TProperty : IBaseProperty { diff --git a/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/PropertyCastWrapper.TSource.TValue.cs b/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/PropertyCastWrapper.TSource.TValue.cs new file mode 100644 index 0000000..c27f7c2 --- /dev/null +++ b/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/PropertyCastWrapper.TSource.TValue.cs @@ -0,0 +1,19 @@ +namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers +{ + internal sealed class PropertyCastWrapper : PropertyWrapper + { + public PropertyCastWrapper() : base(default) + { + } + + protected override TValue Convert(TSource value) + { + return (TValue) (object) value; + } + + protected override TSource ConvertBack(TValue value) + { + return (TSource) (object) value; + } + } +} \ No newline at end of file diff --git a/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/PropertyWrapper.TSource.TValue.cs b/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/PropertyWrapper.TSource.TValue.cs index c553241..e45dc48 100644 --- a/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/PropertyWrapper.TSource.TValue.cs +++ b/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/PropertyWrapper.TSource.TValue.cs @@ -7,7 +7,7 @@ namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers { - internal sealed class PropertyWrapper : IProperty, IPropertyWrapper + internal class PropertyWrapper : IProperty, IPropertyWrapper { private readonly IPropertyValueConverter _valueConverter; @@ -53,14 +53,14 @@ public IPropertyWrapper SetProperty(IBaseProperty property) if (_property is not null) { throw new InvalidOperationException( - $"{nameof(PropertyWrapper)} was not reset."); + $"{nameof(PropertyWrapper)} was not reset."); } _property = (IProperty) property; _property.ValueChanged += OnPropertyValueChanged; _sourceValue = _property.Value; - _value = _valueConverter.Convert(_sourceValue); + _value = Convert(_sourceValue); return this; } @@ -75,7 +75,7 @@ public bool TrySetValue(TValue value) _value = value; - _sourceValue = _valueConverter.ConvertBack(value); + _sourceValue = ConvertBack(value); _property.ForceSetValue(_sourceValue); return true; @@ -94,15 +94,24 @@ private void OnPropertyValueChanged(object sender, TSource sourceValue) if (EqualityComparer.Default.Equals(_sourceValue, sourceValue) == false) { _sourceValue = sourceValue; - _value = _valueConverter.Convert(sourceValue); + _value = Convert(sourceValue); } ValueChanged?.Invoke(this, _value); } - void IProperty.ForceSetValue(TValue value) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected virtual TValue Convert(TSource value) + { + return _valueConverter.Convert(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected virtual TSource ConvertBack(TValue value) { - throw new NotImplementedException(); + return _valueConverter.ConvertBack(value); } + + void IProperty.ForceSetValue(TValue value) => throw new NotImplementedException(); } } \ No newline at end of file diff --git a/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/ReadOnlyPropertyCastWrapper.TSource.TValue.cs b/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/ReadOnlyPropertyCastWrapper.TSource.TValue.cs new file mode 100644 index 0000000..6a81337 --- /dev/null +++ b/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/ReadOnlyPropertyCastWrapper.TSource.TValue.cs @@ -0,0 +1,14 @@ +namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers +{ + internal sealed class ReadOnlyPropertyCastWrapper : ReadOnlyPropertyWrapper + { + public ReadOnlyPropertyCastWrapper() : base(default) + { + } + + protected override TValue Convert(TSource value) + { + return (TValue) (object) value; + } + } +} \ No newline at end of file diff --git a/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/ReadOnlyPropertyWrapper.TSource.TValue.cs b/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/ReadOnlyPropertyWrapper.TSource.TValue.cs index eedd0e5..054b945 100644 --- a/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/ReadOnlyPropertyWrapper.TSource.TValue.cs +++ b/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/ReadOnlyPropertyWrapper.TSource.TValue.cs @@ -6,7 +6,7 @@ namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers { - internal sealed class ReadOnlyPropertyWrapper : IReadOnlyProperty, IPropertyWrapper + internal class ReadOnlyPropertyWrapper : IReadOnlyProperty, IPropertyWrapper { private readonly IPropertyValueConverter _valueConverter; @@ -51,10 +51,10 @@ public IPropertyWrapper SetProperty(IBaseProperty readOnlyProperty) if (_isInitialized) { throw new InvalidOperationException( - $"{nameof(ReadOnlyPropertyWrapper)} was not reset."); + $"{nameof(ReadOnlyPropertyWrapper)} was not reset."); } - _value = _valueConverter.Convert(((IReadOnlyProperty) readOnlyProperty).Value); + _value = Convert(((IReadOnlyProperty) readOnlyProperty).Value); _isInitialized = true; return this; @@ -65,5 +65,11 @@ public void Reset() _value = default; _isInitialized = false; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected virtual TValue Convert(TSource value) + { + return _valueConverter.Convert(value); + } } } \ No newline at end of file diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/BindingContextObjectProvider.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/BindingContextObjectProvider.cs index f936988..ced8566 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/BindingContextObjectProvider.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/BindingContextObjectProvider.cs @@ -86,6 +86,14 @@ public IProperty RentProperty(IBindingContext context, P return GetProperty, TValueType>(context, bindingData); } + public IProperty RentPropertyAs(IBindingContext context, + PropertyBindingData bindingData) + { + EnsureBindingDataValid(bindingData); + + return GetPropertyAs, TValueType>(context, bindingData); + } + public void ReturnProperty(IProperty property) { ReturnBaseProperty(property); @@ -99,6 +107,14 @@ public IReadOnlyProperty RentReadOnlyProperty(IBindingCo return GetProperty, TValueType>(context, bindingData); } + public IReadOnlyProperty RentReadOnlyPropertyAs(IBindingContext context, + PropertyBindingData bindingData) + { + EnsureBindingDataValid(bindingData); + + return GetPropertyAs, TValueType>(context, bindingData); + } + public void ReturnReadOnlyProperty(IReadOnlyProperty property) { ReturnBaseProperty(property); @@ -167,6 +183,18 @@ private TProperty GetProperty(IBindingContext context, Bi return _objectWrapperHandler.GetProperty(context, bindingData, memberInfo); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private TProperty GetPropertyAs(IBindingContext context, BindingData bindingData) + where TProperty : IBaseProperty + { + if (TryGetContextMemberInfo(context.GetType(), bindingData.PropertyName, out var memberInfo) == false) + { + throw new InvalidOperationException($"Property '{bindingData.PropertyName}' not found."); + } + + return _objectWrapperHandler.GetPropertyAs(context, memberInfo); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool TryGetContextMemberInfo(Type contextType, string memberName, out MemberInfo memberInfo) { diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Interfaces/IObjectProvider.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Interfaces/IObjectProvider.cs index 51fb5ef..aec5c3b 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Interfaces/IObjectProvider.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Interfaces/IObjectProvider.cs @@ -15,11 +15,13 @@ IObjectProvider WarmupValueConverter(int capacity, WarmupType warmupType = Wa where T : IValueConverter; IProperty RentProperty(IBindingContext context, PropertyBindingData bindingData); + IProperty RentPropertyAs(IBindingContext context, PropertyBindingData bindingData); void ReturnProperty(IProperty property); IReadOnlyProperty RentReadOnlyProperty(IBindingContext context, PropertyBindingData bindingData); - + IReadOnlyProperty RentReadOnlyPropertyAs(IBindingContext context, + PropertyBindingData bindingData); void ReturnReadOnlyProperty(IReadOnlyProperty property); TCommand GetCommand(IBindingContext context, string propertyName) where TCommand : IBaseCommand; diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs index e8b9d96..543643e 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; +using UnityMvvmToolkit.Core.Converters.PropertyValueConverters; using UnityMvvmToolkit.Core.Enums; using UnityMvvmToolkit.Core.Interfaces; using UnityMvvmToolkit.Core.Internal.Extensions; @@ -50,6 +51,54 @@ public void CreateValueConverterInstances(int capacity, WarmupType warmupType } } + public TProperty GetPropertyAs(IBindingContext context, MemberInfo memberInfo) + where TProperty : IBaseProperty + { + var property = GetMemberValue(context, memberInfo, out var propertyType); + + var targetType = typeof(TValueType); + var sourceType = propertyType.GenericTypeArguments[0]; + + if (targetType == sourceType) + { + return (TProperty) property; + } + + if (targetType.IsValueType || sourceType.IsValueType) + { + throw new InvalidOperationException( + $"{nameof(GetPropertyAs)} is not supported for value types. Use {typeof(PropertyValueConverter<,>).Name} instead."); + } + + if (targetType.IsAssignableFrom(sourceType) == false) + { + throw new InvalidCastException($"Can not cast the '{sourceType}' to the '{targetType}'."); + } + + var converterId = HashCodeHelper.GetPropertyWrapperConverterId(targetType, sourceType); + + if (_wrappersByConverter.TryGetValue(converterId, out var propertyWrappers)) + { + if (propertyWrappers.Count> 0) + { + return (TProperty) propertyWrappers + .Dequeue() + .AsPropertyWrapper() + .SetProperty(property); + } + } + else + { + _wrappersByConverter.Add(converterId, new Queue()); + } + + var wrapperType = property is IProperty + ? typeof(PropertyCastWrapper<,>).MakeGenericType(sourceType, targetType) + : typeof(ReadOnlyPropertyCastWrapper<,>).MakeGenericType(sourceType, targetType); + + return (TProperty) ObjectWrapperHelper.CreatePropertyWrapper(wrapperType, default, converterId, property); + } + public TProperty GetProperty(IBindingContext context, BindingData bindingData, MemberInfo memberInfo) where TProperty : IBaseProperty { diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyCastWrapper.TSource.TValue.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyCastWrapper.TSource.TValue.cs new file mode 100644 index 0000000..c27f7c2 --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyCastWrapper.TSource.TValue.cs @@ -0,0 +1,19 @@ +namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers +{ + internal sealed class PropertyCastWrapper : PropertyWrapper + { + public PropertyCastWrapper() : base(default) + { + } + + protected override TValue Convert(TSource value) + { + return (TValue) (object) value; + } + + protected override TSource ConvertBack(TValue value) + { + return (TSource) (object) value; + } + } +} \ No newline at end of file diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyCastWrapper.TSource.TValue.cs.meta b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyCastWrapper.TSource.TValue.cs.meta new file mode 100644 index 0000000..5a01672 --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyCastWrapper.TSource.TValue.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f9a92007973d8a2419ed0618a27b126d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyWrapper.TSource.TValue.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyWrapper.TSource.TValue.cs index c553241..e45dc48 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyWrapper.TSource.TValue.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyWrapper.TSource.TValue.cs @@ -7,7 +7,7 @@ namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers { - internal sealed class PropertyWrapper : IProperty, IPropertyWrapper + internal class PropertyWrapper : IProperty, IPropertyWrapper { private readonly IPropertyValueConverter _valueConverter; @@ -53,14 +53,14 @@ public IPropertyWrapper SetProperty(IBaseProperty property) if (_property is not null) { throw new InvalidOperationException( - $"{nameof(PropertyWrapper)} was not reset."); + $"{nameof(PropertyWrapper)} was not reset."); } _property = (IProperty) property; _property.ValueChanged += OnPropertyValueChanged; _sourceValue = _property.Value; - _value = _valueConverter.Convert(_sourceValue); + _value = Convert(_sourceValue); return this; } @@ -75,7 +75,7 @@ public bool TrySetValue(TValue value) _value = value; - _sourceValue = _valueConverter.ConvertBack(value); + _sourceValue = ConvertBack(value); _property.ForceSetValue(_sourceValue); return true; @@ -94,15 +94,24 @@ private void OnPropertyValueChanged(object sender, TSource sourceValue) if (EqualityComparer.Default.Equals(_sourceValue, sourceValue) == false) { _sourceValue = sourceValue; - _value = _valueConverter.Convert(sourceValue); + _value = Convert(sourceValue); } ValueChanged?.Invoke(this, _value); } - void IProperty.ForceSetValue(TValue value) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected virtual TValue Convert(TSource value) + { + return _valueConverter.Convert(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected virtual TSource ConvertBack(TValue value) { - throw new NotImplementedException(); + return _valueConverter.ConvertBack(value); } + + void IProperty.ForceSetValue(TValue value) => throw new NotImplementedException(); } } \ No newline at end of file diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyCastWrapper.TSource.TValue.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyCastWrapper.TSource.TValue.cs new file mode 100644 index 0000000..6a81337 --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyCastWrapper.TSource.TValue.cs @@ -0,0 +1,14 @@ +namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers +{ + internal sealed class ReadOnlyPropertyCastWrapper : ReadOnlyPropertyWrapper + { + public ReadOnlyPropertyCastWrapper() : base(default) + { + } + + protected override TValue Convert(TSource value) + { + return (TValue) (object) value; + } + } +} \ No newline at end of file diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyCastWrapper.TSource.TValue.cs.meta b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyCastWrapper.TSource.TValue.cs.meta new file mode 100644 index 0000000..8376eac --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyCastWrapper.TSource.TValue.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4a0b2448c1609784fa9b599ed166db55 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyWrapper.TSource.TValue.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyWrapper.TSource.TValue.cs index eedd0e5..054b945 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyWrapper.TSource.TValue.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyWrapper.TSource.TValue.cs @@ -6,7 +6,7 @@ namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers { - internal sealed class ReadOnlyPropertyWrapper : IReadOnlyProperty, IPropertyWrapper + internal class ReadOnlyPropertyWrapper : IReadOnlyProperty, IPropertyWrapper { private readonly IPropertyValueConverter _valueConverter; @@ -51,10 +51,10 @@ public IPropertyWrapper SetProperty(IBaseProperty readOnlyProperty) if (_isInitialized) { throw new InvalidOperationException( - $"{nameof(ReadOnlyPropertyWrapper)} was not reset."); + $"{nameof(ReadOnlyPropertyWrapper)} was not reset."); } - _value = _valueConverter.Convert(((IReadOnlyProperty) readOnlyProperty).Value); + _value = Convert(((IReadOnlyProperty) readOnlyProperty).Value); _isInitialized = true; return this; @@ -65,5 +65,11 @@ public void Reset() _value = default; _isInitialized = false; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected virtual TValue Convert(TSource value) + { + return _valueConverter.Convert(value); + } } } \ No newline at end of file From 40e7c5b26928f97ec0c1622c665bc9e56825fa13 Mon Sep 17 00:00:00 2001 From: ChebanovDD Date: 2023年7月16日 18:50:24 +0800 Subject: [PATCH 02/10] Add tests for RentPropertyAs. --- .../BindingContextObjectProviderTests.cs | 106 ++++++++++++++++++ .../NestedBindingContext.cs | 7 ++ .../TestBindingContext/NestedClass.cs | 5 + .../ParentBindingContext.cs | 21 ++++ 4 files changed, 139 insertions(+) create mode 100644 tests/UnityMvvmToolkit.Test.Integration/TestBindingContext/NestedBindingContext.cs create mode 100644 tests/UnityMvvmToolkit.Test.Integration/TestBindingContext/NestedClass.cs create mode 100644 tests/UnityMvvmToolkit.Test.Integration/TestBindingContext/ParentBindingContext.cs diff --git a/tests/UnityMvvmToolkit.Test.Integration/BindingContextObjectProviderTests.cs b/tests/UnityMvvmToolkit.Test.Integration/BindingContextObjectProviderTests.cs index 07e22b8..a7ab6d8 100644 --- a/tests/UnityMvvmToolkit.Test.Integration/BindingContextObjectProviderTests.cs +++ b/tests/UnityMvvmToolkit.Test.Integration/BindingContextObjectProviderTests.cs @@ -361,6 +361,88 @@ public void RentProperty_ShouldThrow_WhenConverterIsNotSet() .WithMessage($"Property value converter from '{typeof(int)}' to '{typeof(string)}' not found."); } + [Fact] + public void RentPropertyAs_ShouldReturnProperty_WhenDataIsValid() + { + // Arrange + var objectProvider = new BindingContextObjectProvider(Array.Empty()); + var bindingContext = new ParentBindingContext(); + + var nestedPropertyBindingData = + nameof(ParentBindingContext.NestedPropertyBindingContext).ToPropertyBindingData(); + + // Act + var nestedProperty = + objectProvider.RentPropertyAs(bindingContext, nestedPropertyBindingData); + + // Assert + nestedProperty + .Should() + .NotBeNull() + .And + .BeAssignableTo>() + .And + .BeAssignableTo>(); + } + + [Fact] + public void RentPropertyAs_ShouldReturnValueTypeProperty_WhenDataIsValid() + { + // Arrange + var objectProvider = new BindingContextObjectProvider(Array.Empty()); + var bindingContext = new ParentBindingContext(); + + var floatPropertyBindingData = + nameof(ParentBindingContext.FloatPropertyBindingContext).ToPropertyBindingData(); + + // Act + var floatProperty = objectProvider.RentPropertyAs(bindingContext, floatPropertyBindingData); + + // Assert + floatProperty + .Should() + .NotBeNull() + .And + .BeAssignableTo>() + .And + .BeAssignableTo>(); + } + + [Fact] + public void RentPropertyAs_ShouldThrow_WhenTargetTypeIsNotAssignableFromSourceType() + { + // Arrange + var objectProvider = new BindingContextObjectProvider(Array.Empty()); + var bindingContext = new ParentBindingContext(); + + var nestedPropertyBindingData = nameof(ParentBindingContext.NestedProperty).ToPropertyBindingData(); + + // Assert + objectProvider + .Invoking(sut => sut.RentPropertyAs(bindingContext, nestedPropertyBindingData)) + .Should() + .Throw() + .WithMessage($"Can not cast the '{typeof(NestedClass)}' to the '{typeof(IBindingContext)}'."); + } + + [Fact] + public void RentPropertyAs_ShouldThrow_WheTryingToCastValueTypes() + { + // Arrange + var objectProvider = new BindingContextObjectProvider(Array.Empty()); + var bindingContext = new ParentBindingContext(); + + var floatPropertyBindingData = nameof(ParentBindingContext.FloatPropertyBindingContext).ToPropertyBindingData(); + + // Assert + objectProvider + .Invoking(sut => sut.RentPropertyAs(bindingContext, floatPropertyBindingData)) + .Should() + .Throw() + .WithMessage( + $"{nameof(ObjectWrapperHandler.GetPropertyAs)} is not supported for value types. Use {typeof(PropertyValueConverter<,>).Name} instead."); + } + [Fact] public void RentReadOnlyProperty_ShouldReturnProperty_WhenDataIsValid() { @@ -418,6 +500,30 @@ public void RentReadOnlyProperty_ShouldConvertPropertyValue_WhenSourceAndTargetT boolProperty.Value.Should().Be(resultBoolValue); } + [Fact] + public void RentReadOnlyPropertyAs_ShouldReturnProperty_WhenDataIsValid() + { + // Arrange + var objectProvider = new BindingContextObjectProvider(Array.Empty()); + var bindingContext = new ParentBindingContext(); + + var nestedReadOnlyPropertyBindingData = + nameof(ParentBindingContext.NestedReadOnlyPropertyBindingContext).ToPropertyBindingData(); + + // Act + var nestedReadOnlyProperty = + objectProvider.RentReadOnlyPropertyAs(bindingContext, nestedReadOnlyPropertyBindingData); + + // Assert + nestedReadOnlyProperty + .Should() + .NotBeNull() + .And + .BeAssignableTo>() + .And + .NotBeAssignableTo>(); + } + [Fact] public void RentReadOnlyProperty_ShouldThrow_WhenPropertyTypeIsWrong() { diff --git a/tests/UnityMvvmToolkit.Test.Integration/TestBindingContext/NestedBindingContext.cs b/tests/UnityMvvmToolkit.Test.Integration/TestBindingContext/NestedBindingContext.cs new file mode 100644 index 0000000..62325ea --- /dev/null +++ b/tests/UnityMvvmToolkit.Test.Integration/TestBindingContext/NestedBindingContext.cs @@ -0,0 +1,7 @@ +using UnityMvvmToolkit.Core.Interfaces; + +namespace UnityMvvmToolkit.Test.Integration.TestBindingContext; + +public class NestedBindingContext : IBindingContext +{ +} \ No newline at end of file diff --git a/tests/UnityMvvmToolkit.Test.Integration/TestBindingContext/NestedClass.cs b/tests/UnityMvvmToolkit.Test.Integration/TestBindingContext/NestedClass.cs new file mode 100644 index 0000000..4b382b0 --- /dev/null +++ b/tests/UnityMvvmToolkit.Test.Integration/TestBindingContext/NestedClass.cs @@ -0,0 +1,5 @@ +namespace UnityMvvmToolkit.Test.Integration.TestBindingContext; + +public class NestedClass +{ +} \ No newline at end of file diff --git a/tests/UnityMvvmToolkit.Test.Integration/TestBindingContext/ParentBindingContext.cs b/tests/UnityMvvmToolkit.Test.Integration/TestBindingContext/ParentBindingContext.cs new file mode 100644 index 0000000..f1da3d3 --- /dev/null +++ b/tests/UnityMvvmToolkit.Test.Integration/TestBindingContext/ParentBindingContext.cs @@ -0,0 +1,21 @@ +using UnityMvvmToolkit.Core; +using UnityMvvmToolkit.Core.Interfaces; + +namespace UnityMvvmToolkit.Test.Integration.TestBindingContext; + +public class ParentBindingContext : IBindingContext +{ + public ParentBindingContext() + { + FloatPropertyBindingContext = new Property(); + NestedProperty = new Property(); + + NestedPropertyBindingContext = new Property(new NestedBindingContext()); + NestedReadOnlyPropertyBindingContext = new ReadOnlyProperty(new NestedBindingContext()); + } + + public IProperty FloatPropertyBindingContext { get; } + public IProperty NestedProperty { get; } + public IProperty NestedPropertyBindingContext { get; } + public IReadOnlyProperty NestedReadOnlyPropertyBindingContext { get; } +} \ No newline at end of file From 72d43eed582c1a4a5d2b82c76580fad0a7e6d9ac Mon Sep 17 00:00:00 2001 From: ChebanovDD Date: 2023年7月16日 18:51:27 +0800 Subject: [PATCH 03/10] Resolve #48. Add common BindingContextProvider. --- .../BindingContextProvider.T.cs | 19 +++++++++------ .../BindingContextProvider.cs | 14 +++++++++++ .../BindingContextProvider.cs.meta | 3 +++ .../Uxmls/BindingContextProvider.Uxml.cs | 23 +++++++++++++++++++ .../Uxmls/BindingContextProvider.Uxml.cs.meta | 3 +++ 5 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindingContextProvider.cs create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindingContextProvider.cs.meta create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/Uxmls/BindingContextProvider.Uxml.cs create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/Uxmls/BindingContextProvider.Uxml.cs.meta diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindingContextProvider.T.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindingContextProvider.T.cs index 1a231e6..ab87f1f 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindingContextProvider.T.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindingContextProvider.T.cs @@ -82,16 +82,10 @@ public virtual void ResetBindingContext(IObjectProvider objectProvider) } } - private void OnBindingContextPropertyValueChanged(object sender, TBindingContext bindingContext) - { - SetChildsBindingContext(bindingContext, _objectProvider); - } - protected virtual void OnSetBindingContext(IBindingContext context, IObjectProvider objectProvider, PropertyBindingData propertyBindingData) { - _bindingContextProperty = - objectProvider.RentReadOnlyProperty(context, propertyBindingData); + _bindingContextProperty = RentReadOnlyProperty(context, objectProvider, propertyBindingData); _bindingContextProperty.ValueChanged += OnBindingContextPropertyValueChanged; if (_bindingContextProperty.Value is null) @@ -115,6 +109,17 @@ protected virtual void OnResetBindingContext(IObjectProvider objectProvider) ResetChildsBindingContext(objectProvider); } + private void OnBindingContextPropertyValueChanged(object sender, TBindingContext bindingContext) + { + SetChildsBindingContext(bindingContext, _objectProvider); + } + + protected virtual IReadOnlyProperty RentReadOnlyProperty(IBindingContext context, + IObjectProvider objectProvider, PropertyBindingData propertyBindingData) + { + return objectProvider.RentReadOnlyProperty(context, propertyBindingData); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void SetChildsBindingContext(IBindingContext bindingContext, IObjectProvider objectProvider) { diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindingContextProvider.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindingContextProvider.cs new file mode 100644 index 0000000..e8389ad --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindingContextProvider.cs @@ -0,0 +1,14 @@ +using UnityMvvmToolkit.Core; +using UnityMvvmToolkit.Core.Interfaces; + +namespace UnityMvvmToolkit.UITK.BindableUIElements +{ + public partial class BindingContextProvider : BindingContextProvider + { + protected override IReadOnlyProperty RentReadOnlyProperty(IBindingContext context, + IObjectProvider objectProvider, PropertyBindingData propertyBindingData) + { + return objectProvider.RentReadOnlyPropertyAs(context, propertyBindingData); + } + } +} \ No newline at end of file diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindingContextProvider.cs.meta b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindingContextProvider.cs.meta new file mode 100644 index 0000000..042850d --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindingContextProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7cc76b67851c486f9aeee0a84440da96 +timeCreated: 1689415163 \ No newline at end of file diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/Uxmls/BindingContextProvider.Uxml.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/Uxmls/BindingContextProvider.Uxml.cs new file mode 100644 index 0000000..9ac1b2d --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/Uxmls/BindingContextProvider.Uxml.cs @@ -0,0 +1,23 @@ +using UnityEngine.UIElements; +using UnityMvvmToolkit.Core.Interfaces; + +namespace UnityMvvmToolkit.UITK.BindableUIElements +{ + partial class BindingContextProvider + { + public new class UxmlFactory : UxmlFactory + { + } + +#if UNITY_2023_2_OR_NEWER + [System.Serializable] + public new class UxmlSerializedData : BindingContextProvider.UxmlSerializedData + { + } +#else + public new class UxmlTraits : BindingContextProvider.UxmlTraits + { + } +#endif + } +} \ No newline at end of file diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/Uxmls/BindingContextProvider.Uxml.cs.meta b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/Uxmls/BindingContextProvider.Uxml.cs.meta new file mode 100644 index 0000000..0cdf38b --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/Uxmls/BindingContextProvider.Uxml.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e40cb26c70f34823bdc56111a7192938 +timeCreated: 1689416131 \ No newline at end of file From bcfcd9d54a3c996cdd4aa8e2314e1aad543db4df Mon Sep 17 00:00:00 2001 From: ChebanovDD Date: 2023年7月16日 22:07:35 +0800 Subject: [PATCH 04/10] Add base property wrappers. --- .../ObjectHandlers/ObjectWrapperHandler.cs | 6 ++-- .../PropertyCastWrapper.TSource.TValue.cs | 10 +++---- .../PropertyConvertWrapper.TSource.TValue.cs | 29 +++++++++++++++++++ .../PropertyWrapper.TSource.TValue.cs | 17 +++-------- ...dOnlyPropertyCastWrapper.TSource.TValue.cs | 9 +++--- ...lyPropertyConvertWrapper.TSource.TValue.cs | 23 +++++++++++++++ .../ReadOnlyPropertyWrapper.TSource.TValue.cs | 12 ++------ .../ObjectHandlers/ObjectWrapperHandler.cs | 6 ++-- .../PropertyCastWrapper.TSource.TValue.cs | 10 +++---- .../PropertyConvertWrapper.TSource.TValue.cs | 29 +++++++++++++++++++ ...pertyConvertWrapper.TSource.TValue.cs.meta | 11 +++++++ .../PropertyWrapper.TSource.TValue.cs | 17 +++-------- ...dOnlyPropertyCastWrapper.TSource.TValue.cs | 9 +++--- ...lyPropertyConvertWrapper.TSource.TValue.cs | 23 +++++++++++++++ ...pertyConvertWrapper.TSource.TValue.cs.meta | 11 +++++++ .../ReadOnlyPropertyWrapper.TSource.TValue.cs | 12 ++------ .../BindingContextObjectProviderTests.cs | 2 +- .../PropertyTests.cs | 14 --------- .../PropertyWrapperTests.cs | 24 +++++++-------- 19 files changed, 177 insertions(+), 97 deletions(-) create mode 100644 src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/PropertyConvertWrapper.TSource.TValue.cs create mode 100644 src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/ReadOnlyPropertyConvertWrapper.TSource.TValue.cs create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyConvertWrapper.TSource.TValue.cs create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyConvertWrapper.TSource.TValue.cs.meta create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyConvertWrapper.TSource.TValue.cs create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyConvertWrapper.TSource.TValue.cs.meta diff --git a/src/UnityMvvmToolkit.Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs b/src/UnityMvvmToolkit.Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs index 543643e..e57e542 100644 --- a/src/UnityMvvmToolkit.Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs +++ b/src/UnityMvvmToolkit.Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs @@ -139,8 +139,8 @@ public TProperty GetProperty(IBindingContext context, Bin var args = new object[] { valueConverter }; var wrapperType = property is IProperty - ? typeof(PropertyWrapper<,>).MakeGenericType(sourceType, targetType) - : typeof(ReadOnlyPropertyWrapper<,>).MakeGenericType(sourceType, targetType); + ? typeof(PropertyConvertWrapper<,>).MakeGenericType(sourceType, targetType) + : typeof(ReadOnlyPropertyConvertWrapper<,>).MakeGenericType(sourceType, targetType); return (TProperty) ObjectWrapperHelper.CreatePropertyWrapper(wrapperType, args, converterId, property); } @@ -281,7 +281,7 @@ private void CreatePropertyValueConverterInstances(int converterId, IPropertyVal var itemsQueue = new Queue(); var args = new object[] { converter }; - var wrapperType = typeof(PropertyWrapper<,>).MakeGenericType(converter.SourceType, converter.TargetType); + var wrapperType = typeof(PropertyConvertWrapper<,>).MakeGenericType(converter.SourceType, converter.TargetType); for (var i = 0; i < capacity; i++) { diff --git a/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/PropertyCastWrapper.TSource.TValue.cs b/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/PropertyCastWrapper.TSource.TValue.cs index c27f7c2..f10d62d 100644 --- a/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/PropertyCastWrapper.TSource.TValue.cs +++ b/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/PropertyCastWrapper.TSource.TValue.cs @@ -1,16 +1,16 @@ -namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers +using System.Runtime.CompilerServices; + +namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers { internal sealed class PropertyCastWrapper : PropertyWrapper { - public PropertyCastWrapper() : base(default) - { - } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override TValue Convert(TSource value) { return (TValue) (object) value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override TSource ConvertBack(TValue value) { return (TSource) (object) value; diff --git a/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/PropertyConvertWrapper.TSource.TValue.cs b/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/PropertyConvertWrapper.TSource.TValue.cs new file mode 100644 index 0000000..39c0d12 --- /dev/null +++ b/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/PropertyConvertWrapper.TSource.TValue.cs @@ -0,0 +1,29 @@ +using System.Runtime.CompilerServices; +using UnityMvvmToolkit.Core.Attributes; +using UnityMvvmToolkit.Core.Interfaces; + +namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers +{ + internal sealed class PropertyConvertWrapper : PropertyWrapper + { + private readonly IPropertyValueConverter _valueConverter; + + [Preserve] + public PropertyConvertWrapper(IPropertyValueConverter valueConverter) + { + _valueConverter = valueConverter; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override TValue Convert(TSource value) + { + return _valueConverter.Convert(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override TSource ConvertBack(TValue value) + { + return _valueConverter.ConvertBack(value); + } + } +} \ No newline at end of file diff --git a/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/PropertyWrapper.TSource.TValue.cs b/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/PropertyWrapper.TSource.TValue.cs index e45dc48..d3dd900 100644 --- a/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/PropertyWrapper.TSource.TValue.cs +++ b/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/PropertyWrapper.TSource.TValue.cs @@ -7,10 +7,8 @@ namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers { - internal class PropertyWrapper : IProperty, IPropertyWrapper + internal abstract class PropertyWrapper : IProperty, IPropertyWrapper { - private readonly IPropertyValueConverter _valueConverter; - private int _converterId; private TValue _value; @@ -18,10 +16,9 @@ internal class PropertyWrapper : IProperty, IPropertyWr private IProperty _property; [Preserve] - public PropertyWrapper(IPropertyValueConverter valueConverter) + protected PropertyWrapper() { _converterId = -1; - _valueConverter = valueConverter; } public int ConverterId => _converterId; @@ -101,16 +98,10 @@ private void OnPropertyValueChanged(object sender, TSource sourceValue) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected virtual TValue Convert(TSource value) - { - return _valueConverter.Convert(value); - } + protected abstract TValue Convert(TSource value); [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected virtual TSource ConvertBack(TValue value) - { - return _valueConverter.ConvertBack(value); - } + protected abstract TSource ConvertBack(TValue value); void IProperty.ForceSetValue(TValue value) => throw new NotImplementedException(); } diff --git a/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/ReadOnlyPropertyCastWrapper.TSource.TValue.cs b/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/ReadOnlyPropertyCastWrapper.TSource.TValue.cs index 6a81337..207ced0 100644 --- a/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/ReadOnlyPropertyCastWrapper.TSource.TValue.cs +++ b/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/ReadOnlyPropertyCastWrapper.TSource.TValue.cs @@ -1,11 +1,10 @@ -namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers +using System.Runtime.CompilerServices; + +namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers { internal sealed class ReadOnlyPropertyCastWrapper : ReadOnlyPropertyWrapper { - public ReadOnlyPropertyCastWrapper() : base(default) - { - } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override TValue Convert(TSource value) { return (TValue) (object) value; diff --git a/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/ReadOnlyPropertyConvertWrapper.TSource.TValue.cs b/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/ReadOnlyPropertyConvertWrapper.TSource.TValue.cs new file mode 100644 index 0000000..688094d --- /dev/null +++ b/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/ReadOnlyPropertyConvertWrapper.TSource.TValue.cs @@ -0,0 +1,23 @@ +using System.Runtime.CompilerServices; +using UnityMvvmToolkit.Core.Attributes; +using UnityMvvmToolkit.Core.Interfaces; + +namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers +{ + internal sealed class ReadOnlyPropertyConvertWrapper : ReadOnlyPropertyWrapper + { + private readonly IPropertyValueConverter _valueConverter; + + [Preserve] + public ReadOnlyPropertyConvertWrapper(IPropertyValueConverter valueConverter) + { + _valueConverter = valueConverter; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override TValue Convert(TSource value) + { + return _valueConverter.Convert(value); + } + } +} \ No newline at end of file diff --git a/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/ReadOnlyPropertyWrapper.TSource.TValue.cs b/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/ReadOnlyPropertyWrapper.TSource.TValue.cs index 054b945..b8b6a8f 100644 --- a/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/ReadOnlyPropertyWrapper.TSource.TValue.cs +++ b/src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/ReadOnlyPropertyWrapper.TSource.TValue.cs @@ -6,20 +6,17 @@ namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers { - internal class ReadOnlyPropertyWrapper : IReadOnlyProperty, IPropertyWrapper + internal abstract class ReadOnlyPropertyWrapper : IReadOnlyProperty, IPropertyWrapper { - private readonly IPropertyValueConverter _valueConverter; - private int _converterId; private bool _isInitialized; private TValue _value; [Preserve] - public ReadOnlyPropertyWrapper(IPropertyValueConverter valueConverter) + protected ReadOnlyPropertyWrapper() { _converterId = -1; - _valueConverter = valueConverter; } public int ConverterId => _converterId; @@ -67,9 +64,6 @@ public void Reset() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected virtual TValue Convert(TSource value) - { - return _valueConverter.Convert(value); - } + protected abstract TValue Convert(TSource value); } } \ No newline at end of file diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs index 543643e..e57e542 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs @@ -139,8 +139,8 @@ public TProperty GetProperty(IBindingContext context, Bin var args = new object[] { valueConverter }; var wrapperType = property is IProperty - ? typeof(PropertyWrapper<,>).MakeGenericType(sourceType, targetType) - : typeof(ReadOnlyPropertyWrapper<,>).MakeGenericType(sourceType, targetType); + ? typeof(PropertyConvertWrapper<,>).MakeGenericType(sourceType, targetType) + : typeof(ReadOnlyPropertyConvertWrapper<,>).MakeGenericType(sourceType, targetType); return (TProperty) ObjectWrapperHelper.CreatePropertyWrapper(wrapperType, args, converterId, property); } @@ -281,7 +281,7 @@ private void CreatePropertyValueConverterInstances(int converterId, IPropertyVal var itemsQueue = new Queue(); var args = new object[] { converter }; - var wrapperType = typeof(PropertyWrapper<,>).MakeGenericType(converter.SourceType, converter.TargetType); + var wrapperType = typeof(PropertyConvertWrapper<,>).MakeGenericType(converter.SourceType, converter.TargetType); for (var i = 0; i < capacity; i++) { diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyCastWrapper.TSource.TValue.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyCastWrapper.TSource.TValue.cs index c27f7c2..f10d62d 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyCastWrapper.TSource.TValue.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyCastWrapper.TSource.TValue.cs @@ -1,16 +1,16 @@ -namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers +using System.Runtime.CompilerServices; + +namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers { internal sealed class PropertyCastWrapper : PropertyWrapper { - public PropertyCastWrapper() : base(default) - { - } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override TValue Convert(TSource value) { return (TValue) (object) value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override TSource ConvertBack(TValue value) { return (TSource) (object) value; diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyConvertWrapper.TSource.TValue.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyConvertWrapper.TSource.TValue.cs new file mode 100644 index 0000000..39c0d12 --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyConvertWrapper.TSource.TValue.cs @@ -0,0 +1,29 @@ +using System.Runtime.CompilerServices; +using UnityMvvmToolkit.Core.Attributes; +using UnityMvvmToolkit.Core.Interfaces; + +namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers +{ + internal sealed class PropertyConvertWrapper : PropertyWrapper + { + private readonly IPropertyValueConverter _valueConverter; + + [Preserve] + public PropertyConvertWrapper(IPropertyValueConverter valueConverter) + { + _valueConverter = valueConverter; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override TValue Convert(TSource value) + { + return _valueConverter.Convert(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override TSource ConvertBack(TValue value) + { + return _valueConverter.ConvertBack(value); + } + } +} \ No newline at end of file diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyConvertWrapper.TSource.TValue.cs.meta b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyConvertWrapper.TSource.TValue.cs.meta new file mode 100644 index 0000000..10224c6 --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyConvertWrapper.TSource.TValue.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a8fad497455a75f4d98c9a1215c34888 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyWrapper.TSource.TValue.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyWrapper.TSource.TValue.cs index e45dc48..d3dd900 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyWrapper.TSource.TValue.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/PropertyWrapper.TSource.TValue.cs @@ -7,10 +7,8 @@ namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers { - internal class PropertyWrapper : IProperty, IPropertyWrapper + internal abstract class PropertyWrapper : IProperty, IPropertyWrapper { - private readonly IPropertyValueConverter _valueConverter; - private int _converterId; private TValue _value; @@ -18,10 +16,9 @@ internal class PropertyWrapper : IProperty, IPropertyWr private IProperty _property; [Preserve] - public PropertyWrapper(IPropertyValueConverter valueConverter) + protected PropertyWrapper() { _converterId = -1; - _valueConverter = valueConverter; } public int ConverterId => _converterId; @@ -101,16 +98,10 @@ private void OnPropertyValueChanged(object sender, TSource sourceValue) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected virtual TValue Convert(TSource value) - { - return _valueConverter.Convert(value); - } + protected abstract TValue Convert(TSource value); [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected virtual TSource ConvertBack(TValue value) - { - return _valueConverter.ConvertBack(value); - } + protected abstract TSource ConvertBack(TValue value); void IProperty.ForceSetValue(TValue value) => throw new NotImplementedException(); } diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyCastWrapper.TSource.TValue.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyCastWrapper.TSource.TValue.cs index 6a81337..207ced0 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyCastWrapper.TSource.TValue.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyCastWrapper.TSource.TValue.cs @@ -1,11 +1,10 @@ -namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers +using System.Runtime.CompilerServices; + +namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers { internal sealed class ReadOnlyPropertyCastWrapper : ReadOnlyPropertyWrapper { - public ReadOnlyPropertyCastWrapper() : base(default) - { - } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override TValue Convert(TSource value) { return (TValue) (object) value; diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyConvertWrapper.TSource.TValue.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyConvertWrapper.TSource.TValue.cs new file mode 100644 index 0000000..688094d --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyConvertWrapper.TSource.TValue.cs @@ -0,0 +1,23 @@ +using System.Runtime.CompilerServices; +using UnityMvvmToolkit.Core.Attributes; +using UnityMvvmToolkit.Core.Interfaces; + +namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers +{ + internal sealed class ReadOnlyPropertyConvertWrapper : ReadOnlyPropertyWrapper + { + private readonly IPropertyValueConverter _valueConverter; + + [Preserve] + public ReadOnlyPropertyConvertWrapper(IPropertyValueConverter valueConverter) + { + _valueConverter = valueConverter; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override TValue Convert(TSource value) + { + return _valueConverter.Convert(value); + } + } +} \ No newline at end of file diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyConvertWrapper.TSource.TValue.cs.meta b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyConvertWrapper.TSource.TValue.cs.meta new file mode 100644 index 0000000..cbb7c42 --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyConvertWrapper.TSource.TValue.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 086fbde29babb0440acd27dcf27fd0b1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyWrapper.TSource.TValue.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyWrapper.TSource.TValue.cs index 054b945..b8b6a8f 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyWrapper.TSource.TValue.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectWrappers/ReadOnlyPropertyWrapper.TSource.TValue.cs @@ -6,20 +6,17 @@ namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers { - internal class ReadOnlyPropertyWrapper : IReadOnlyProperty, IPropertyWrapper + internal abstract class ReadOnlyPropertyWrapper : IReadOnlyProperty, IPropertyWrapper { - private readonly IPropertyValueConverter _valueConverter; - private int _converterId; private bool _isInitialized; private TValue _value; [Preserve] - public ReadOnlyPropertyWrapper(IPropertyValueConverter valueConverter) + protected ReadOnlyPropertyWrapper() { _converterId = -1; - _valueConverter = valueConverter; } public int ConverterId => _converterId; @@ -67,9 +64,6 @@ public void Reset() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected virtual TValue Convert(TSource value) - { - return _valueConverter.Convert(value); - } + protected abstract TValue Convert(TSource value); } } \ No newline at end of file diff --git a/tests/UnityMvvmToolkit.Test.Integration/BindingContextObjectProviderTests.cs b/tests/UnityMvvmToolkit.Test.Integration/BindingContextObjectProviderTests.cs index a7ab6d8..dc14df4 100644 --- a/tests/UnityMvvmToolkit.Test.Integration/BindingContextObjectProviderTests.cs +++ b/tests/UnityMvvmToolkit.Test.Integration/BindingContextObjectProviderTests.cs @@ -814,7 +814,7 @@ public void Dispose_ShouldThrow_WhenWarmupValueConverter() public void Dispose_ShouldThrow_WhenReturnProperty() { // Arrange - var property = new PropertyWrapper(default); + var property = new PropertyConvertWrapper(default); var objectProvider = new BindingContextObjectProvider(Array.Empty()); // Act diff --git a/tests/UnityMvvmToolkit.Test.Unit/PropertyTests.cs b/tests/UnityMvvmToolkit.Test.Unit/PropertyTests.cs index 610f5ab..71ab791 100644 --- a/tests/UnityMvvmToolkit.Test.Unit/PropertyTests.cs +++ b/tests/UnityMvvmToolkit.Test.Unit/PropertyTests.cs @@ -1,9 +1,7 @@ using System.Diagnostics.CodeAnalysis; using FluentAssertions; using UnityMvvmToolkit.Core; -using UnityMvvmToolkit.Core.Converters.PropertyValueConverters; using UnityMvvmToolkit.Core.Interfaces; -using UnityMvvmToolkit.Core.Internal.ObjectWrappers; namespace UnityMvvmToolkit.Test.Unit; @@ -179,22 +177,10 @@ public void Property_ShouldImplicitlyConvertValue() private static IEnumerable PropertyDataSets(int defaultValue) { yield return new object[] { new Property(defaultValue), defaultValue }; - - var converter = new IntToStrConverter(); - var propertyWrapper = new PropertyWrapper(converter) - .SetProperty(new Property(defaultValue)); - - yield return new object[] { propertyWrapper, converter.Convert(defaultValue) }; } private static IEnumerable PropertyWithSetValueDataSets(int defaultValue, int valueToSet) { yield return new object[] { new Property(defaultValue), defaultValue, valueToSet }; - - var converter = new IntToStrConverter(); - var propertyWrapper = new PropertyWrapper(converter) - .SetProperty(new Property(defaultValue)); - - yield return new object[] { propertyWrapper, converter.Convert(defaultValue), valueToSet }; } } \ No newline at end of file diff --git a/tests/UnityMvvmToolkit.Test.Unit/PropertyWrapperTests.cs b/tests/UnityMvvmToolkit.Test.Unit/PropertyWrapperTests.cs index bc2b6b0..cfb293a 100644 --- a/tests/UnityMvvmToolkit.Test.Unit/PropertyWrapperTests.cs +++ b/tests/UnityMvvmToolkit.Test.Unit/PropertyWrapperTests.cs @@ -19,7 +19,7 @@ public PropertyWrapperTests() public void PropertyWrapper_ShouldCreateNewPropertyWrapperInstance() { // Act - var propertyWrapper = new PropertyWrapper(_intToStrConverter); + var propertyWrapper = new PropertyConvertWrapper(_intToStrConverter); // Assert propertyWrapper.ConverterId.Should().Be(-1); @@ -32,7 +32,7 @@ public void SetConverterId_ShouldSetButNotResetConverterId_WhenConverterIdIsNotS const int converterId = 5; var property = new Property(); - var propertyWrapper = new PropertyWrapper(_intToStrConverter); + var propertyWrapper = new PropertyConvertWrapper(_intToStrConverter); // Act propertyWrapper @@ -51,7 +51,7 @@ public void SetConverterId_ShouldThrow_WhenConverterIdAlreadySet() // Arrange const int converterId = 5; - var propertyWrapper = new PropertyWrapper(_intToStrConverter); + var propertyWrapper = new PropertyConvertWrapper(_intToStrConverter); // Act propertyWrapper.SetConverterId(converterId); @@ -71,7 +71,7 @@ public void SetProperty_ShouldSetProperty_WhenPropertyIsNotSet() const int propertyValue = 55; var property = new Property(propertyValue); - var propertyWrapper = new PropertyWrapper(_intToStrConverter); + var propertyWrapper = new PropertyConvertWrapper(_intToStrConverter); // Act propertyWrapper.SetProperty(property); @@ -89,7 +89,7 @@ public void SetProperty_ShouldThrow_WhenPropertyAlreadySet() { // Arrange var property = new Property(); - var propertyWrapper = new PropertyWrapper(_intToStrConverter); + var propertyWrapper = new PropertyConvertWrapper(_intToStrConverter); // Act propertyWrapper.SetProperty(property); @@ -113,7 +113,7 @@ public void Reset_ShouldResetPropertyAndValue_WhenPropertyIsNotNull() string? expectedValue = default; var property = new Property(propertyValue); - var propertyWrapper = new PropertyWrapper(_intToStrConverter); + var propertyWrapper = new PropertyConvertWrapper(_intToStrConverter); propertyWrapper.ValueChanged += (_, newValue) => { @@ -137,7 +137,7 @@ public void Reset_ShouldResetPropertyAndValue_WhenPropertyIsNotNull() public void Reset_ShouldThrow_WhenPropertyIsNull() { // Arrange - var propertyWrapper = new PropertyWrapper(_intToStrConverter); + var propertyWrapper = new PropertyConvertWrapper(_intToStrConverter); // Assert propertyWrapper @@ -150,7 +150,7 @@ public void Reset_ShouldThrow_WhenPropertyIsNull() public void ForceSetValue_ShouldThrow() { // Arrange - IProperty propertyWrapper = new PropertyWrapper(_intToStrConverter); + IProperty propertyWrapper = new PropertyConvertWrapper(_intToStrConverter); // Assert propertyWrapper @@ -169,7 +169,7 @@ public void SetValue_ShouldRaisePropertyWrapperValueChangedEvent_WhenPropertyVal string? expectedValue = default; var property = new Property(); - var propertyWrapper = new PropertyWrapper(_intToStrConverter); + var propertyWrapper = new PropertyConvertWrapper(_intToStrConverter); propertyWrapper.ValueChanged += (_, newValue) => { @@ -197,7 +197,7 @@ public void SetValue_ShouldNotRaisePropertyWrapperValueChangedEvent_WhenProperty string? expectedValue = default; var property = new Property(valueToSet); - var propertyWrapper = new PropertyWrapper(_intToStrConverter); + var propertyWrapper = new PropertyConvertWrapper(_intToStrConverter); propertyWrapper.ValueChanged += (_, newValue) => { @@ -225,7 +225,7 @@ public void SetValue_ShouldRaisePropertyValueChangedEvent_WhenPropertyWrapperVal int expectedValue = default; var property = new Property(); - var propertyWrapper = new PropertyWrapper(_intToStrConverter); + var propertyWrapper = new PropertyConvertWrapper(_intToStrConverter); property.ValueChanged += (_, newValue) => { @@ -252,7 +252,7 @@ public void SetValue_ShouldNotRaisePropertyValueChangedEvent_WhenPropertyWrapper int expectedValue = default; var property = new Property(valueToSet); - var propertyWrapper = new PropertyWrapper(_intToStrConverter); + var propertyWrapper = new PropertyConvertWrapper(_intToStrConverter); property.ValueChanged += (_, newValue) => { From e3dbe6c49eb4c46259adbe77b84f38a80f367b96 Mon Sep 17 00:00:00 2001 From: ChebanovDD Date: 2023年7月16日 22:33:56 +0800 Subject: [PATCH 05/10] Add ItemsSource property to collections. --- .../BindableListView.TItem.TCollection.cs | 11 +++++++---- .../UITK/BindableUIElements/BindableScrollView.T.cs | 12 ++++++++---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableListView.TItem.TCollection.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableListView.TItem.TCollection.cs index f6199f7..5b04477 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableListView.TItem.TCollection.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableListView.TItem.TCollection.cs @@ -19,12 +19,15 @@ public abstract partial class BindableListView private VisualTreeAsset _itemTemplate; private IObjectProvider _objectProvider; + private List _itemAssets; private IList _itemsSource; private Dictionary _activeItems; - protected PropertyBindingData _itemsSourceBindingData; - protected IReadOnlyProperty _itemsSourceProperty; + private PropertyBindingData _itemsSourceBindingData; + private IReadOnlyProperty _itemsSourceProperty; + + protected TCollection ItemsSource => _itemsSourceProperty is null ? default : _itemsSourceProperty.Value; public virtual void Initialize() { @@ -34,9 +37,9 @@ public virtual void Initialize() public virtual void Dispose() { - for (var i = 0; i < _itemAssets.Count; i++) + foreach (var itemAsset in _itemAssets) { - _itemAssets[i].DisposeBindableElement(_objectProvider); + itemAsset.DisposeBindableElement(_objectProvider); } _itemAssets.Clear(); diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableScrollView.T.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableScrollView.T.cs index b5e4dbe..d5b9091 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableScrollView.T.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableScrollView.T.cs @@ -13,20 +13,24 @@ namespace UnityMvvmToolkit.UITK.BindableUIElements { - public abstract partial class BindableScrollView : ScrollView, IBindableCollection, - IInitializable, IDisposable where TItemBindingContext : ICollectionItem + public abstract partial class BindableScrollView : + ScrollView, IBindableCollection, IInitializable, IDisposable + where TItemBindingContext : ICollectionItem { private int _itemsCount; private VisualTreeAsset _itemTemplate; private IObjectProvider _objectProvider; + private ObjectPool _itemAssetsPool; private Dictionary _itemAssets; private ObservableCollection _itemsSource; - protected PropertyBindingData _itemsSourceBindingData; - protected IReadOnlyProperty> _itemsSourceProperty; + private PropertyBindingData _itemsSourceBindingData; + private IReadOnlyProperty> _itemsSourceProperty; + + protected ObservableCollection ItemsSource => _itemsSourceProperty?.Value; public virtual void Initialize() { From 3d3c8feb660bd51f7930833bd5ac5b3ec24c36d4 Mon Sep 17 00:00:00 2001 From: ChebanovDD Date: 2023年7月16日 22:34:13 +0800 Subject: [PATCH 06/10] Refactoring. --- .../BindingContextProvider.T.cs | 21 ++++++++++--------- .../BindingContextProvider.cs | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindingContextProvider.T.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindingContextProvider.T.cs index ab87f1f..51f91a1 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindingContextProvider.T.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindingContextProvider.T.cs @@ -49,9 +49,9 @@ public virtual void Dispose() return; } - for (var i = 0; i < _bindableElements.Count; i++) + foreach (var bindableElement in _bindableElements) { - if (_bindableElements[i] is IDisposable disposable) + if (bindableElement is IDisposable disposable) { disposable.Dispose(); } @@ -85,7 +85,7 @@ public virtual void ResetBindingContext(IObjectProvider objectProvider) protected virtual void OnSetBindingContext(IBindingContext context, IObjectProvider objectProvider, PropertyBindingData propertyBindingData) { - _bindingContextProperty = RentReadOnlyProperty(context, objectProvider, propertyBindingData); + _bindingContextProperty = RentBindingContextProperty(context, objectProvider, propertyBindingData); _bindingContextProperty.ValueChanged += OnBindingContextPropertyValueChanged; if (_bindingContextProperty.Value is null) @@ -103,10 +103,11 @@ protected virtual void OnResetBindingContext(IObjectProvider objectProvider) objectProvider.ReturnReadOnlyProperty(_bindingContextProperty); - _bindingContextProperty = null; - BindingContext = default; ResetChildsBindingContext(objectProvider); + + _objectProvider = default; + _bindingContextProperty = default; } private void OnBindingContextPropertyValueChanged(object sender, TBindingContext bindingContext) @@ -114,7 +115,7 @@ private void OnBindingContextPropertyValueChanged(object sender, TBindingContext SetChildsBindingContext(bindingContext, _objectProvider); } - protected virtual IReadOnlyProperty RentReadOnlyProperty(IBindingContext context, + protected virtual IReadOnlyProperty RentBindingContextProperty(IBindingContext context, IObjectProvider objectProvider, PropertyBindingData propertyBindingData) { return objectProvider.RentReadOnlyProperty(context, propertyBindingData); @@ -123,18 +124,18 @@ protected virtual IReadOnlyProperty RentReadOnlyProperty(IBindi [MethodImpl(MethodImplOptions.AggressiveInlining)] private void SetChildsBindingContext(IBindingContext bindingContext, IObjectProvider objectProvider) { - for (var i = 0; i < _bindableElements.Count; i++) + foreach (var bindableElement in _bindableElements) { - _bindableElements[i].SetBindingContext(bindingContext, objectProvider); + bindableElement.SetBindingContext(bindingContext, objectProvider); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ResetChildsBindingContext(IObjectProvider objectProvider) { - for (var i = 0; i < _bindableElements.Count; i++) + foreach (var bindableElement in _bindableElements) { - _bindableElements[i].ResetBindingContext(objectProvider); + bindableElement.ResetBindingContext(objectProvider); } } } diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindingContextProvider.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindingContextProvider.cs index e8389ad..cde543a 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindingContextProvider.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindingContextProvider.cs @@ -5,7 +5,7 @@ namespace UnityMvvmToolkit.UITK.BindableUIElements { public partial class BindingContextProvider : BindingContextProvider { - protected override IReadOnlyProperty RentReadOnlyProperty(IBindingContext context, + protected override IReadOnlyProperty RentBindingContextProperty(IBindingContext context, IObjectProvider objectProvider, PropertyBindingData propertyBindingData) { return objectProvider.RentReadOnlyPropertyAs(context, propertyBindingData); From 02e2c1494ebaef29aa1bc83f1a473ba5205f5681 Mon Sep 17 00:00:00 2001 From: ChebanovDD Date: 2023年7月19日 23:44:16 +0800 Subject: [PATCH 07/10] Close #50. Add ReadOnlyProperty support for BindableTextField. --- .../BindingContextObjectProvider.cs | 25 ++++++++++ .../Interfaces/IObjectProvider.cs | 3 ++ .../Extensions/MemberInfoExtensions.cs | 36 +++++++++++++++ .../ObjectHandlers/ObjectWrapperHandler.cs | 34 ++------------ .../Core/BindingContextObjectProvider.cs | 25 ++++++++++ .../Core/Interfaces/IObjectProvider.cs | 3 ++ .../Extensions/MemberInfoExtensions.cs | 36 +++++++++++++++ .../Extensions/MemberInfoExtensions.cs.meta | 11 +++++ .../ObjectHandlers/ObjectWrapperHandler.cs | 34 ++------------ .../BindableUIElements/BindableTextField.cs | 46 +++++++++++++++---- 10 files changed, 183 insertions(+), 70 deletions(-) create mode 100644 src/UnityMvvmToolkit.Core/Internal/Extensions/MemberInfoExtensions.cs create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/Extensions/MemberInfoExtensions.cs create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/Extensions/MemberInfoExtensions.cs.meta diff --git a/src/UnityMvvmToolkit.Core/BindingContextObjectProvider.cs b/src/UnityMvvmToolkit.Core/BindingContextObjectProvider.cs index ced8566..3dd07c9 100644 --- a/src/UnityMvvmToolkit.Core/BindingContextObjectProvider.cs +++ b/src/UnityMvvmToolkit.Core/BindingContextObjectProvider.cs @@ -79,6 +79,31 @@ public IObjectProvider WarmupValueConverter(int capacity, WarmupType warmupTy return this; } + public bool TryRentProperty(IBindingContext context, PropertyBindingData bindingData, + out IProperty property) + { + EnsureBindingDataValid(bindingData); + + if (TryGetContextMemberInfo(context.GetType(), bindingData.PropertyName, out var memberInfo) == false) + { + property = default; + return false; + } + + var baseProperty = memberInfo.GetMemberValue(context, out _); + + if (baseProperty is IProperty) + { + property = _objectWrapperHandler + .GetProperty, TValueType>(context, bindingData, memberInfo); + + return true; + } + + property = default; + return false; + } + public IProperty RentProperty(IBindingContext context, PropertyBindingData bindingData) { EnsureBindingDataValid(bindingData); diff --git a/src/UnityMvvmToolkit.Core/Interfaces/IObjectProvider.cs b/src/UnityMvvmToolkit.Core/Interfaces/IObjectProvider.cs index aec5c3b..f5d413c 100644 --- a/src/UnityMvvmToolkit.Core/Interfaces/IObjectProvider.cs +++ b/src/UnityMvvmToolkit.Core/Interfaces/IObjectProvider.cs @@ -14,6 +14,9 @@ public interface IObjectProvider IObjectProvider WarmupValueConverter(int capacity, WarmupType warmupType = WarmupType.OnlyByType) where T : IValueConverter; + bool TryRentProperty(IBindingContext context, PropertyBindingData bindingData, + out IProperty property); + IProperty RentProperty(IBindingContext context, PropertyBindingData bindingData); IProperty RentPropertyAs(IBindingContext context, PropertyBindingData bindingData); void ReturnProperty(IProperty property); diff --git a/src/UnityMvvmToolkit.Core/Internal/Extensions/MemberInfoExtensions.cs b/src/UnityMvvmToolkit.Core/Internal/Extensions/MemberInfoExtensions.cs new file mode 100644 index 0000000..59a1a5e --- /dev/null +++ b/src/UnityMvvmToolkit.Core/Internal/Extensions/MemberInfoExtensions.cs @@ -0,0 +1,36 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using UnityMvvmToolkit.Core.Interfaces; + +namespace UnityMvvmToolkit.Core.Internal.Extensions +{ + internal static class MemberInfoExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T GetMemberValue(this MemberInfo memberInfo, IBindingContext context, out Type memberType) + { + switch (memberInfo.MemberType) + { + case MemberTypes.Field: + { + var fieldInfo = (FieldInfo) memberInfo; + memberType = fieldInfo.FieldType; + + return (T) fieldInfo.GetValue(context); + } + + case MemberTypes.Property: + { + var propertyInfo = (PropertyInfo) memberInfo; + memberType = propertyInfo.PropertyType; + + return (T) propertyInfo.GetValue(context); + } + + default: + throw new ArgumentOutOfRangeException(); + } + } + } +} \ No newline at end of file diff --git a/src/UnityMvvmToolkit.Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs b/src/UnityMvvmToolkit.Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs index e57e542..d1f3650 100644 --- a/src/UnityMvvmToolkit.Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs +++ b/src/UnityMvvmToolkit.Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs @@ -54,7 +54,7 @@ public void CreateValueConverterInstances(int capacity, WarmupType warmupType public TProperty GetPropertyAs(IBindingContext context, MemberInfo memberInfo) where TProperty : IBaseProperty { - var property = GetMemberValue(context, memberInfo, out var propertyType); + var property = memberInfo.GetMemberValue(context, out var propertyType); var targetType = typeof(TValueType); var sourceType = propertyType.GenericTypeArguments[0]; @@ -102,7 +102,7 @@ public TProperty GetPropertyAs(IBindingContext context, M public TProperty GetProperty(IBindingContext context, BindingData bindingData, MemberInfo memberInfo) where TProperty : IBaseProperty { - var property = GetMemberValue(context, memberInfo, out var propertyType); + var property = memberInfo.GetMemberValue(context, out var propertyType); var targetType = typeof(TValueType); var sourceType = propertyType.GenericTypeArguments[0]; @@ -148,13 +148,13 @@ public TProperty GetProperty(IBindingContext context, Bin public TCommand GetCommand(IBindingContext context, MemberInfo memberInfo) where TCommand : IBaseCommand { - return GetMemberValue(context, memberInfo, out _); + return memberInfo.GetMemberValue(context, out _); } public ICommandWrapper GetCommandWrapper(IBindingContext context, CommandBindingData bindingData, MemberInfo memberInfo) { - var command = GetMemberValue(context, memberInfo, out var commandType); + var command = memberInfo.GetMemberValue(context, out var commandType); if (commandType.IsGenericType == false || commandType.GetInterface(nameof(IBaseCommand)) == null) @@ -353,32 +353,6 @@ private void ReturnWrapper(IObjectWrapper wrapper) _wrappersByConverter[wrapper.ConverterId].Enqueue(wrapper); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static T GetMemberValue(IBindingContext context, MemberInfo memberInfo, out Type memberType) - { - switch (memberInfo.MemberType) - { - case MemberTypes.Field: - { - var fieldInfo = (FieldInfo) memberInfo; - memberType = fieldInfo.FieldType; - - return (T) fieldInfo.GetValue(context); - } - - case MemberTypes.Property: - { - var propertyInfo = (PropertyInfo) memberInfo; - memberType = propertyInfo.PropertyType; - - return (T) propertyInfo.GetValue(context); - } - - default: - throw new ArgumentOutOfRangeException(); - } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private void AssureIsNotDisposed() { diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/BindingContextObjectProvider.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/BindingContextObjectProvider.cs index ced8566..3dd07c9 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/BindingContextObjectProvider.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/BindingContextObjectProvider.cs @@ -79,6 +79,31 @@ public IObjectProvider WarmupValueConverter(int capacity, WarmupType warmupTy return this; } + public bool TryRentProperty(IBindingContext context, PropertyBindingData bindingData, + out IProperty property) + { + EnsureBindingDataValid(bindingData); + + if (TryGetContextMemberInfo(context.GetType(), bindingData.PropertyName, out var memberInfo) == false) + { + property = default; + return false; + } + + var baseProperty = memberInfo.GetMemberValue(context, out _); + + if (baseProperty is IProperty) + { + property = _objectWrapperHandler + .GetProperty, TValueType>(context, bindingData, memberInfo); + + return true; + } + + property = default; + return false; + } + public IProperty RentProperty(IBindingContext context, PropertyBindingData bindingData) { EnsureBindingDataValid(bindingData); diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Interfaces/IObjectProvider.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Interfaces/IObjectProvider.cs index aec5c3b..f5d413c 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Interfaces/IObjectProvider.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Interfaces/IObjectProvider.cs @@ -14,6 +14,9 @@ public interface IObjectProvider IObjectProvider WarmupValueConverter(int capacity, WarmupType warmupType = WarmupType.OnlyByType) where T : IValueConverter; + bool TryRentProperty(IBindingContext context, PropertyBindingData bindingData, + out IProperty property); + IProperty RentProperty(IBindingContext context, PropertyBindingData bindingData); IProperty RentPropertyAs(IBindingContext context, PropertyBindingData bindingData); void ReturnProperty(IProperty property); diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/Extensions/MemberInfoExtensions.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/Extensions/MemberInfoExtensions.cs new file mode 100644 index 0000000..59a1a5e --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/Extensions/MemberInfoExtensions.cs @@ -0,0 +1,36 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using UnityMvvmToolkit.Core.Interfaces; + +namespace UnityMvvmToolkit.Core.Internal.Extensions +{ + internal static class MemberInfoExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T GetMemberValue(this MemberInfo memberInfo, IBindingContext context, out Type memberType) + { + switch (memberInfo.MemberType) + { + case MemberTypes.Field: + { + var fieldInfo = (FieldInfo) memberInfo; + memberType = fieldInfo.FieldType; + + return (T) fieldInfo.GetValue(context); + } + + case MemberTypes.Property: + { + var propertyInfo = (PropertyInfo) memberInfo; + memberType = propertyInfo.PropertyType; + + return (T) propertyInfo.GetValue(context); + } + + default: + throw new ArgumentOutOfRangeException(); + } + } + } +} \ No newline at end of file diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/Extensions/MemberInfoExtensions.cs.meta b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/Extensions/MemberInfoExtensions.cs.meta new file mode 100644 index 0000000..546bd47 --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/Extensions/MemberInfoExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 08d32f20c35e29042b46beef22248b34 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs index e57e542..d1f3650 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs @@ -54,7 +54,7 @@ public void CreateValueConverterInstances(int capacity, WarmupType warmupType public TProperty GetPropertyAs(IBindingContext context, MemberInfo memberInfo) where TProperty : IBaseProperty { - var property = GetMemberValue(context, memberInfo, out var propertyType); + var property = memberInfo.GetMemberValue(context, out var propertyType); var targetType = typeof(TValueType); var sourceType = propertyType.GenericTypeArguments[0]; @@ -102,7 +102,7 @@ public TProperty GetPropertyAs(IBindingContext context, M public TProperty GetProperty(IBindingContext context, BindingData bindingData, MemberInfo memberInfo) where TProperty : IBaseProperty { - var property = GetMemberValue(context, memberInfo, out var propertyType); + var property = memberInfo.GetMemberValue(context, out var propertyType); var targetType = typeof(TValueType); var sourceType = propertyType.GenericTypeArguments[0]; @@ -148,13 +148,13 @@ public TProperty GetProperty(IBindingContext context, Bin public TCommand GetCommand(IBindingContext context, MemberInfo memberInfo) where TCommand : IBaseCommand { - return GetMemberValue(context, memberInfo, out _); + return memberInfo.GetMemberValue(context, out _); } public ICommandWrapper GetCommandWrapper(IBindingContext context, CommandBindingData bindingData, MemberInfo memberInfo) { - var command = GetMemberValue(context, memberInfo, out var commandType); + var command = memberInfo.GetMemberValue(context, out var commandType); if (commandType.IsGenericType == false || commandType.GetInterface(nameof(IBaseCommand)) == null) @@ -353,32 +353,6 @@ private void ReturnWrapper(IObjectWrapper wrapper) _wrappersByConverter[wrapper.ConverterId].Enqueue(wrapper); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static T GetMemberValue(IBindingContext context, MemberInfo memberInfo, out Type memberType) - { - switch (memberInfo.MemberType) - { - case MemberTypes.Field: - { - var fieldInfo = (FieldInfo) memberInfo; - memberType = fieldInfo.FieldType; - - return (T) fieldInfo.GetValue(context); - } - - case MemberTypes.Property: - { - var propertyInfo = (PropertyInfo) memberInfo; - memberType = propertyInfo.PropertyType; - - return (T) propertyInfo.GetValue(context); - } - - default: - throw new ArgumentOutOfRangeException(); - } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private void AssureIsNotDisposed() { diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableTextField.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableTextField.cs index c7a7bca..efb0436 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableTextField.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableTextField.cs @@ -1,4 +1,5 @@ -using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; using UnityEngine.UIElements; using UnityMvvmToolkit.Core; using UnityMvvmToolkit.Core.Extensions; @@ -8,9 +9,11 @@ namespace UnityMvvmToolkit.UITK.BindableUIElements { public partial class BindableTextField : TextField, IBindableElement { - private IProperty _valueProperty; private PropertyBindingData _propertyBindingData; + private IProperty _valueProperty; + private IReadOnlyProperty _valueReadOnlyProperty; + public virtual void SetBindingContext(IBindingContext context, IObjectProvider objectProvider) { if (string.IsNullOrWhiteSpace(BindingValuePath)) @@ -20,27 +23,50 @@ public virtual void SetBindingContext(IBindingContext context, IObjectProvider o _propertyBindingData ??= BindingValuePath.ToPropertyBindingData(); - _valueProperty = objectProvider.RentProperty(context, _propertyBindingData); - _valueProperty.ValueChanged += OnPropertyValueChanged; + if (objectProvider.TryRentProperty(context, _propertyBindingData, out _valueProperty)) + { + _valueReadOnlyProperty = _valueProperty; + this.RegisterValueChangedCallback(OnControlValueChanged); + } + else if (isReadOnly) + { + _valueReadOnlyProperty = objectProvider.RentReadOnlyProperty(context, _propertyBindingData); + } + else + { + var controlName = string.IsNullOrWhiteSpace(name) ? nameof(BindableTextField) : name; - UpdateControlValue(_valueProperty.Value); - this.RegisterValueChangedCallback(OnControlValueChanged); + throw new InvalidOperationException( + $"The {_propertyBindingData.PropertyName} property is read-only. Mark the {controlName} as read-only or change the property type."); + } + + _valueReadOnlyProperty.ValueChanged += OnPropertyValueChanged; + + UpdateControlValue(_valueReadOnlyProperty.Value); } public virtual void ResetBindingContext(IObjectProvider objectProvider) { - if (_valueProperty is null) + if (_valueReadOnlyProperty is null) { return; } - _valueProperty.ValueChanged -= OnPropertyValueChanged; + _valueReadOnlyProperty.ValueChanged -= OnPropertyValueChanged; - objectProvider.ReturnProperty(_valueProperty); + if (_valueProperty is null) + { + objectProvider.ReturnReadOnlyProperty(_valueReadOnlyProperty); + } + else + { + objectProvider.ReturnProperty(_valueProperty); + this.UnregisterValueChangedCallback(OnControlValueChanged); + } _valueProperty = null; + _valueReadOnlyProperty = null; - this.UnregisterValueChangedCallback(OnControlValueChanged); UpdateControlValue(default); } From 85c61f64c7bffab028d6ca036050d2bc676b63e1 Mon Sep 17 00:00:00 2001 From: ChebanovDD Date: 2023年7月20日 11:20:47 +0800 Subject: [PATCH 08/10] Fix TryRentProperty issue. --- src/UnityMvvmToolkit.Core/BindingContextObjectProvider.cs | 2 +- .../Runtime/Core/BindingContextObjectProvider.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/UnityMvvmToolkit.Core/BindingContextObjectProvider.cs b/src/UnityMvvmToolkit.Core/BindingContextObjectProvider.cs index 3dd07c9..dec8f04 100644 --- a/src/UnityMvvmToolkit.Core/BindingContextObjectProvider.cs +++ b/src/UnityMvvmToolkit.Core/BindingContextObjectProvider.cs @@ -92,7 +92,7 @@ public bool TryRentProperty(IBindingContext context, PropertyBinding var baseProperty = memberInfo.GetMemberValue(context, out _); - if (baseProperty is IProperty) + if (baseProperty is IProperty) { property = _objectWrapperHandler .GetProperty, TValueType>(context, bindingData, memberInfo); diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/BindingContextObjectProvider.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/BindingContextObjectProvider.cs index 3dd07c9..dec8f04 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/BindingContextObjectProvider.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/BindingContextObjectProvider.cs @@ -92,7 +92,7 @@ public bool TryRentProperty(IBindingContext context, PropertyBinding var baseProperty = memberInfo.GetMemberValue(context, out _); - if (baseProperty is IProperty) + if (baseProperty is IProperty) { property = _objectWrapperHandler .GetProperty, TValueType>(context, bindingData, memberInfo); From 7aec6027ae70effeda7dc04a6fcee3d37a73aa81 Mon Sep 17 00:00:00 2001 From: ChebanovDD Date: 2023年7月20日 15:21:42 +0800 Subject: [PATCH 09/10] Bump package version. --- .../Assets/Plugins/UnityMvvmToolkit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/package.json b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/package.json index 0353af9..72abc63 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/package.json +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/package.json @@ -2,7 +2,7 @@ "name": "com.chebanovdd.unitymvvmtoolkit", "displayName": "Unity MVVM Toolkit", "author": { "name": "ChebanovDD", "url": "https://github.com/ChebanovDD" }, - "version": "1.1.6", + "version": "1.1.7", "unity": "2018.4", "description": "The Unity Mvvm Toolkit allows you to use data binding to establish a connection between the app UI and the data it displays. This is a simple and consistent way to achieve clean separation of business logic from UI.", "keywords": [ "mvvm", "binding", "ui", "toolkit" ], From d3da804fe1154e780bc709a13ad44ce0860a20ef Mon Sep 17 00:00:00 2001 From: ChebanovDD Date: 2023年7月20日 15:35:49 +0800 Subject: [PATCH 10/10] Add tests for TryRentProperty. --- .../BindingContextObjectProviderTests.cs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tests/UnityMvvmToolkit.Test.Integration/BindingContextObjectProviderTests.cs b/tests/UnityMvvmToolkit.Test.Integration/BindingContextObjectProviderTests.cs index dc14df4..aed0b21 100644 --- a/tests/UnityMvvmToolkit.Test.Integration/BindingContextObjectProviderTests.cs +++ b/tests/UnityMvvmToolkit.Test.Integration/BindingContextObjectProviderTests.cs @@ -149,6 +149,55 @@ public void WarmupValueConverter_ShouldThrow_WhenValueConverterWasWarmup() .WithMessage("Warm up only during the initialization phase."); } + [Fact] + public void TryRentProperty_ShouldReturnProperty_WhenDataIsValid() + { + // Arrange + const int countValue = 69; + + var objectProvider = new BindingContextObjectProvider(Array.Empty()); + var bindingContext = new MyBindingContext + { + Count = countValue, + }; + + var countPropertyBindingData = nameof(MyBindingContext.Count).ToPropertyBindingData(); + + // Act + var result = + objectProvider.TryRentProperty(bindingContext, countPropertyBindingData, out var countProperty); + + // Assert + countProperty + .Should() + .NotBeNull() + .And + .BeAssignableTo>() + .And + .BeAssignableTo>(); + + result.Should().BeTrue(); + countProperty.Value.Should().Be(countValue); + } + + [Fact] + public void TryRentProperty_ShouldNotReturnProperty_WhenPropertyIsReadOnly() + { + // Arrange + var objectProvider = new BindingContextObjectProvider(Array.Empty()); + var bindingContext = new MyBindingContext(); + + var readOnlyPropertyBindingData = nameof(MyBindingContext.IntReadOnlyValue).ToPropertyBindingData(); + + // Act + var result = + objectProvider.TryRentProperty(bindingContext, readOnlyPropertyBindingData, out var countProperty); + + // Assert + result.Should().BeFalse(); + countProperty.Should().BeNull(); + } + [Fact] public void RentProperty_ShouldReturnProperty_WhenDataIsValid() {

AltStyle によって変換されたページ (->オリジナル) /