Skip to content

Commit ec14c65

Browse files
committed
Resolve #48. Add PropertyCastWrappers.
1 parent 3fda705 commit ec14c65

16 files changed

+298
-22
lines changed

src/UnityMvvmToolkit.Core/BindingContextObjectProvider.cs

+28
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ public IProperty<TValueType> RentProperty<TValueType>(IBindingContext context, P
8686
return GetProperty<IProperty<TValueType>, TValueType>(context, bindingData);
8787
}
8888

89+
public IProperty<TValueType> RentPropertyAs<TValueType>(IBindingContext context,
90+
PropertyBindingData bindingData)
91+
{
92+
EnsureBindingDataValid(bindingData);
93+
94+
return GetPropertyAs<IProperty<TValueType>, TValueType>(context, bindingData);
95+
}
96+
8997
public void ReturnProperty<TValueType>(IProperty<TValueType> property)
9098
{
9199
ReturnBaseProperty(property);
@@ -99,6 +107,14 @@ public IReadOnlyProperty<TValueType> RentReadOnlyProperty<TValueType>(IBindingCo
99107
return GetProperty<IReadOnlyProperty<TValueType>, TValueType>(context, bindingData);
100108
}
101109

110+
public IReadOnlyProperty<TValueType> RentReadOnlyPropertyAs<TValueType>(IBindingContext context,
111+
PropertyBindingData bindingData)
112+
{
113+
EnsureBindingDataValid(bindingData);
114+
115+
return GetPropertyAs<IReadOnlyProperty<TValueType>, TValueType>(context, bindingData);
116+
}
117+
102118
public void ReturnReadOnlyProperty<TValueType>(IReadOnlyProperty<TValueType> property)
103119
{
104120
ReturnBaseProperty(property);
@@ -167,6 +183,18 @@ private TProperty GetProperty<TProperty, TValueType>(IBindingContext context, Bi
167183
return _objectWrapperHandler.GetProperty<TProperty, TValueType>(context, bindingData, memberInfo);
168184
}
169185

186+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
187+
private TProperty GetPropertyAs<TProperty, TValueType>(IBindingContext context, BindingData bindingData)
188+
where TProperty : IBaseProperty
189+
{
190+
if (TryGetContextMemberInfo(context.GetType(), bindingData.PropertyName, out var memberInfo) == false)
191+
{
192+
throw new InvalidOperationException($"Property '{bindingData.PropertyName}' not found.");
193+
}
194+
195+
return _objectWrapperHandler.GetPropertyAs<TProperty, TValueType>(context, memberInfo);
196+
}
197+
170198
[MethodImpl(MethodImplOptions.AggressiveInlining)]
171199
private bool TryGetContextMemberInfo(Type contextType, string memberName, out MemberInfo memberInfo)
172200
{

src/UnityMvvmToolkit.Core/Interfaces/IObjectProvider.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ IObjectProvider WarmupValueConverter<T>(int capacity, WarmupType warmupType = Wa
1515
where T : IValueConverter;
1616

1717
IProperty<TValueType> RentProperty<TValueType>(IBindingContext context, PropertyBindingData bindingData);
18+
IProperty<TValueType> RentPropertyAs<TValueType>(IBindingContext context, PropertyBindingData bindingData);
1819
void ReturnProperty<TValueType>(IProperty<TValueType> property);
1920

2021
IReadOnlyProperty<TValueType> RentReadOnlyProperty<TValueType>(IBindingContext context,
2122
PropertyBindingData bindingData);
22-
23+
IReadOnlyProperty<TValueType> RentReadOnlyPropertyAs<TValueType>(IBindingContext context,
24+
PropertyBindingData bindingData);
2325
void ReturnReadOnlyProperty<TValueType>(IReadOnlyProperty<TValueType> property);
2426

2527
TCommand GetCommand<TCommand>(IBindingContext context, string propertyName) where TCommand : IBaseCommand;

src/UnityMvvmToolkit.Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs

+49
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Reflection;
44
using System.Runtime.CompilerServices;
5+
using UnityMvvmToolkit.Core.Converters.PropertyValueConverters;
56
using UnityMvvmToolkit.Core.Enums;
67
using UnityMvvmToolkit.Core.Interfaces;
78
using UnityMvvmToolkit.Core.Internal.Extensions;
@@ -50,6 +51,54 @@ public void CreateValueConverterInstances<T>(int capacity, WarmupType warmupType
5051
}
5152
}
5253

54+
public TProperty GetPropertyAs<TProperty, TValueType>(IBindingContext context, MemberInfo memberInfo)
55+
where TProperty : IBaseProperty
56+
{
57+
var property = GetMemberValue<IBaseProperty>(context, memberInfo, out var propertyType);
58+
59+
var targetType = typeof(TValueType);
60+
var sourceType = propertyType.GenericTypeArguments[0];
61+
62+
if (targetType == sourceType)
63+
{
64+
return (TProperty) property;
65+
}
66+
67+
if (targetType.IsValueType || sourceType.IsValueType)
68+
{
69+
throw new InvalidOperationException(
70+
$"{nameof(GetPropertyAs)} is not supported for value types. Use {typeof(PropertyValueConverter<,>).Name} instead.");
71+
}
72+
73+
if (targetType.IsAssignableFrom(sourceType) == false)
74+
{
75+
throw new InvalidCastException($"Can not cast the '{sourceType}' to the '{targetType}'.");
76+
}
77+
78+
var converterId = HashCodeHelper.GetPropertyWrapperConverterId(targetType, sourceType);
79+
80+
if (_wrappersByConverter.TryGetValue(converterId, out var propertyWrappers))
81+
{
82+
if (propertyWrappers.Count > 0)
83+
{
84+
return (TProperty) propertyWrappers
85+
.Dequeue()
86+
.AsPropertyWrapper()
87+
.SetProperty(property);
88+
}
89+
}
90+
else
91+
{
92+
_wrappersByConverter.Add(converterId, new Queue<IObjectWrapper>());
93+
}
94+
95+
var wrapperType = property is IProperty
96+
? typeof(PropertyCastWrapper<,>).MakeGenericType(sourceType, targetType)
97+
: typeof(ReadOnlyPropertyCastWrapper<,>).MakeGenericType(sourceType, targetType);
98+
99+
return (TProperty) ObjectWrapperHelper.CreatePropertyWrapper(wrapperType, default, converterId, property);
100+
}
101+
53102
public TProperty GetProperty<TProperty, TValueType>(IBindingContext context, BindingData bindingData,
54103
MemberInfo memberInfo) where TProperty : IBaseProperty
55104
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers
2+
{
3+
internal sealed class PropertyCastWrapper<TSource, TValue> : PropertyWrapper<TSource, TValue>
4+
{
5+
public PropertyCastWrapper() : base(default)
6+
{
7+
}
8+
9+
protected override TValue Convert(TSource value)
10+
{
11+
return (TValue) (object) value;
12+
}
13+
14+
protected override TSource ConvertBack(TValue value)
15+
{
16+
return (TSource) (object) value;
17+
}
18+
}
19+
}

src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/PropertyWrapper.TSource.TValue.cs

+16-7
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers
99
{
10-
internal sealed class PropertyWrapper<TSource, TValue> : IProperty<TValue>, IPropertyWrapper
10+
internal class PropertyWrapper<TSource, TValue> : IProperty<TValue>, IPropertyWrapper
1111
{
1212
private readonly IPropertyValueConverter<TSource, TValue> _valueConverter;
1313

@@ -53,14 +53,14 @@ public IPropertyWrapper SetProperty(IBaseProperty property)
5353
if (_property is not null)
5454
{
5555
throw new InvalidOperationException(
56-
$"{nameof(PropertyWrapper<TValue, TSource>)} was not reset.");
56+
$"{nameof(PropertyWrapper<TSource, TValue>)} was not reset.");
5757
}
5858

5959
_property = (IProperty<TSource>) property;
6060
_property.ValueChanged += OnPropertyValueChanged;
6161

6262
_sourceValue = _property.Value;
63-
_value = _valueConverter.Convert(_sourceValue);
63+
_value = Convert(_sourceValue);
6464

6565
return this;
6666
}
@@ -75,7 +75,7 @@ public bool TrySetValue(TValue value)
7575

7676
_value = value;
7777

78-
_sourceValue = _valueConverter.ConvertBack(value);
78+
_sourceValue = ConvertBack(value);
7979
_property.ForceSetValue(_sourceValue);
8080

8181
return true;
@@ -94,15 +94,24 @@ private void OnPropertyValueChanged(object sender, TSource sourceValue)
9494
if (EqualityComparer<TSource>.Default.Equals(_sourceValue, sourceValue) == false)
9595
{
9696
_sourceValue = sourceValue;
97-
_value = _valueConverter.Convert(sourceValue);
97+
_value = Convert(sourceValue);
9898
}
9999

100100
ValueChanged?.Invoke(this, _value);
101101
}
102102

103-
void IProperty<TValue>.ForceSetValue(TValue value)
103+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
104+
protected virtual TValue Convert(TSource value)
105+
{
106+
return _valueConverter.Convert(value);
107+
}
108+
109+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
110+
protected virtual TSource ConvertBack(TValue value)
104111
{
105-
throw new NotImplementedException();
112+
return _valueConverter.ConvertBack(value);
106113
}
114+
115+
void IProperty<TValue>.ForceSetValue(TValue value) => throw new NotImplementedException();
107116
}
108117
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers
2+
{
3+
internal sealed class ReadOnlyPropertyCastWrapper<TSource, TValue> : ReadOnlyPropertyWrapper<TSource, TValue>
4+
{
5+
public ReadOnlyPropertyCastWrapper() : base(default)
6+
{
7+
}
8+
9+
protected override TValue Convert(TSource value)
10+
{
11+
return (TValue) (object) value;
12+
}
13+
}
14+
}

src/UnityMvvmToolkit.Core/Internal/ObjectWrappers/ReadOnlyPropertyWrapper.TSource.TValue.cs

+9-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers
88
{
9-
internal sealed class ReadOnlyPropertyWrapper<TSource, TValue> : IReadOnlyProperty<TValue>, IPropertyWrapper
9+
internal class ReadOnlyPropertyWrapper<TSource, TValue> : IReadOnlyProperty<TValue>, IPropertyWrapper
1010
{
1111
private readonly IPropertyValueConverter<TSource, TValue> _valueConverter;
1212

@@ -51,10 +51,10 @@ public IPropertyWrapper SetProperty(IBaseProperty readOnlyProperty)
5151
if (_isInitialized)
5252
{
5353
throw new InvalidOperationException(
54-
$"{nameof(ReadOnlyPropertyWrapper<TValue, TSource>)} was not reset.");
54+
$"{nameof(ReadOnlyPropertyWrapper<TSource, TValue>)} was not reset.");
5555
}
5656

57-
_value = _valueConverter.Convert(((IReadOnlyProperty<TSource>) readOnlyProperty).Value);
57+
_value = Convert(((IReadOnlyProperty<TSource>) readOnlyProperty).Value);
5858
_isInitialized = true;
5959

6060
return this;
@@ -65,5 +65,11 @@ public void Reset()
6565
_value = default;
6666
_isInitialized = false;
6767
}
68+
69+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
70+
protected virtual TValue Convert(TSource value)
71+
{
72+
return _valueConverter.Convert(value);
73+
}
6874
}
6975
}

src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/BindingContextObjectProvider.cs

+28
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ public IProperty<TValueType> RentProperty<TValueType>(IBindingContext context, P
8686
return GetProperty<IProperty<TValueType>, TValueType>(context, bindingData);
8787
}
8888

89+
public IProperty<TValueType> RentPropertyAs<TValueType>(IBindingContext context,
90+
PropertyBindingData bindingData)
91+
{
92+
EnsureBindingDataValid(bindingData);
93+
94+
return GetPropertyAs<IProperty<TValueType>, TValueType>(context, bindingData);
95+
}
96+
8997
public void ReturnProperty<TValueType>(IProperty<TValueType> property)
9098
{
9199
ReturnBaseProperty(property);
@@ -99,6 +107,14 @@ public IReadOnlyProperty<TValueType> RentReadOnlyProperty<TValueType>(IBindingCo
99107
return GetProperty<IReadOnlyProperty<TValueType>, TValueType>(context, bindingData);
100108
}
101109

110+
public IReadOnlyProperty<TValueType> RentReadOnlyPropertyAs<TValueType>(IBindingContext context,
111+
PropertyBindingData bindingData)
112+
{
113+
EnsureBindingDataValid(bindingData);
114+
115+
return GetPropertyAs<IReadOnlyProperty<TValueType>, TValueType>(context, bindingData);
116+
}
117+
102118
public void ReturnReadOnlyProperty<TValueType>(IReadOnlyProperty<TValueType> property)
103119
{
104120
ReturnBaseProperty(property);
@@ -167,6 +183,18 @@ private TProperty GetProperty<TProperty, TValueType>(IBindingContext context, Bi
167183
return _objectWrapperHandler.GetProperty<TProperty, TValueType>(context, bindingData, memberInfo);
168184
}
169185

186+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
187+
private TProperty GetPropertyAs<TProperty, TValueType>(IBindingContext context, BindingData bindingData)
188+
where TProperty : IBaseProperty
189+
{
190+
if (TryGetContextMemberInfo(context.GetType(), bindingData.PropertyName, out var memberInfo) == false)
191+
{
192+
throw new InvalidOperationException($"Property '{bindingData.PropertyName}' not found.");
193+
}
194+
195+
return _objectWrapperHandler.GetPropertyAs<TProperty, TValueType>(context, memberInfo);
196+
}
197+
170198
[MethodImpl(MethodImplOptions.AggressiveInlining)]
171199
private bool TryGetContextMemberInfo(Type contextType, string memberName, out MemberInfo memberInfo)
172200
{

src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Interfaces/IObjectProvider.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ IObjectProvider WarmupValueConverter<T>(int capacity, WarmupType warmupType = Wa
1515
where T : IValueConverter;
1616

1717
IProperty<TValueType> RentProperty<TValueType>(IBindingContext context, PropertyBindingData bindingData);
18+
IProperty<TValueType> RentPropertyAs<TValueType>(IBindingContext context, PropertyBindingData bindingData);
1819
void ReturnProperty<TValueType>(IProperty<TValueType> property);
1920

2021
IReadOnlyProperty<TValueType> RentReadOnlyProperty<TValueType>(IBindingContext context,
2122
PropertyBindingData bindingData);
22-
23+
IReadOnlyProperty<TValueType> RentReadOnlyPropertyAs<TValueType>(IBindingContext context,
24+
PropertyBindingData bindingData);
2325
void ReturnReadOnlyProperty<TValueType>(IReadOnlyProperty<TValueType> property);
2426

2527
TCommand GetCommand<TCommand>(IBindingContext context, string propertyName) where TCommand : IBaseCommand;

src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Core/Internal/ObjectHandlers/ObjectWrapperHandler.cs

+49
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Reflection;
44
using System.Runtime.CompilerServices;
5+
using UnityMvvmToolkit.Core.Converters.PropertyValueConverters;
56
using UnityMvvmToolkit.Core.Enums;
67
using UnityMvvmToolkit.Core.Interfaces;
78
using UnityMvvmToolkit.Core.Internal.Extensions;
@@ -50,6 +51,54 @@ public void CreateValueConverterInstances<T>(int capacity, WarmupType warmupType
5051
}
5152
}
5253

54+
public TProperty GetPropertyAs<TProperty, TValueType>(IBindingContext context, MemberInfo memberInfo)
55+
where TProperty : IBaseProperty
56+
{
57+
var property = GetMemberValue<IBaseProperty>(context, memberInfo, out var propertyType);
58+
59+
var targetType = typeof(TValueType);
60+
var sourceType = propertyType.GenericTypeArguments[0];
61+
62+
if (targetType == sourceType)
63+
{
64+
return (TProperty) property;
65+
}
66+
67+
if (targetType.IsValueType || sourceType.IsValueType)
68+
{
69+
throw new InvalidOperationException(
70+
$"{nameof(GetPropertyAs)} is not supported for value types. Use {typeof(PropertyValueConverter<,>).Name} instead.");
71+
}
72+
73+
if (targetType.IsAssignableFrom(sourceType) == false)
74+
{
75+
throw new InvalidCastException($"Can not cast the '{sourceType}' to the '{targetType}'.");
76+
}
77+
78+
var converterId = HashCodeHelper.GetPropertyWrapperConverterId(targetType, sourceType);
79+
80+
if (_wrappersByConverter.TryGetValue(converterId, out var propertyWrappers))
81+
{
82+
if (propertyWrappers.Count > 0)
83+
{
84+
return (TProperty) propertyWrappers
85+
.Dequeue()
86+
.AsPropertyWrapper()
87+
.SetProperty(property);
88+
}
89+
}
90+
else
91+
{
92+
_wrappersByConverter.Add(converterId, new Queue<IObjectWrapper>());
93+
}
94+
95+
var wrapperType = property is IProperty
96+
? typeof(PropertyCastWrapper<,>).MakeGenericType(sourceType, targetType)
97+
: typeof(ReadOnlyPropertyCastWrapper<,>).MakeGenericType(sourceType, targetType);
98+
99+
return (TProperty) ObjectWrapperHelper.CreatePropertyWrapper(wrapperType, default, converterId, property);
100+
}
101+
53102
public TProperty GetProperty<TProperty, TValueType>(IBindingContext context, BindingData bindingData,
54103
MemberInfo memberInfo) where TProperty : IBaseProperty
55104
{

0 commit comments

Comments
 (0)