Skip to content

New functionality. #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions src/UnityMvvmToolkit.Core/BindingContextObjectProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,46 @@ public IObjectProvider WarmupValueConverter<T>(int capacity, WarmupType warmupTy
return this;
}

public bool TryRentProperty<TValueType>(IBindingContext context, PropertyBindingData bindingData,
out IProperty<TValueType> property)
{
EnsureBindingDataValid(bindingData);

if (TryGetContextMemberInfo(context.GetType(), bindingData.PropertyName, out var memberInfo) == false)
{
property = default;
return false;
}

var baseProperty = memberInfo.GetMemberValue<IBaseProperty>(context, out _);

if (baseProperty is IProperty)
{
property = _objectWrapperHandler
.GetProperty<IProperty<TValueType>, TValueType>(context, bindingData, memberInfo);

return true;
}

property = default;
return false;
}

public IProperty<TValueType> RentProperty<TValueType>(IBindingContext context, PropertyBindingData bindingData)
{
EnsureBindingDataValid(bindingData);

return GetProperty<IProperty<TValueType>, TValueType>(context, bindingData);
}

public IProperty<TValueType> RentPropertyAs<TValueType>(IBindingContext context,
PropertyBindingData bindingData)
{
EnsureBindingDataValid(bindingData);

return GetPropertyAs<IProperty<TValueType>, TValueType>(context, bindingData);
}

public void ReturnProperty<TValueType>(IProperty<TValueType> property)
{
ReturnBaseProperty(property);
Expand All @@ -99,6 +132,14 @@ public IReadOnlyProperty<TValueType> RentReadOnlyProperty<TValueType>(IBindingCo
return GetProperty<IReadOnlyProperty<TValueType>, TValueType>(context, bindingData);
}

public IReadOnlyProperty<TValueType> RentReadOnlyPropertyAs<TValueType>(IBindingContext context,
PropertyBindingData bindingData)
{
EnsureBindingDataValid(bindingData);

return GetPropertyAs<IReadOnlyProperty<TValueType>, TValueType>(context, bindingData);
}

public void ReturnReadOnlyProperty<TValueType>(IReadOnlyProperty<TValueType> property)
{
ReturnBaseProperty(property);
Expand Down Expand Up @@ -167,6 +208,18 @@ private TProperty GetProperty<TProperty, TValueType>(IBindingContext context, Bi
return _objectWrapperHandler.GetProperty<TProperty, TValueType>(context, bindingData, memberInfo);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private TProperty GetPropertyAs<TProperty, TValueType>(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<TProperty, TValueType>(context, memberInfo);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool TryGetContextMemberInfo(Type contextType, string memberName, out MemberInfo memberInfo)
{
Expand Down
7 changes: 6 additions & 1 deletion src/UnityMvvmToolkit.Core/Interfaces/IObjectProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@ public interface IObjectProvider
IObjectProvider WarmupValueConverter<T>(int capacity, WarmupType warmupType = WarmupType.OnlyByType)
where T : IValueConverter;

bool TryRentProperty<TValueType>(IBindingContext context, PropertyBindingData bindingData,
out IProperty<TValueType> property);

IProperty<TValueType> RentProperty<TValueType>(IBindingContext context, PropertyBindingData bindingData);
IProperty<TValueType> RentPropertyAs<TValueType>(IBindingContext context, PropertyBindingData bindingData);
void ReturnProperty<TValueType>(IProperty<TValueType> property);

IReadOnlyProperty<TValueType> RentReadOnlyProperty<TValueType>(IBindingContext context,
PropertyBindingData bindingData);

IReadOnlyProperty<TValueType> RentReadOnlyPropertyAs<TValueType>(IBindingContext context,
PropertyBindingData bindingData);
void ReturnReadOnlyProperty<TValueType>(IReadOnlyProperty<TValueType> property);

TCommand GetCommand<TCommand>(IBindingContext context, string propertyName) where TCommand : IBaseCommand;
Expand Down
Original file line number Diff line number Diff line change
@@ -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<T>(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();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -50,10 +51,58 @@ public void CreateValueConverterInstances<T>(int capacity, WarmupType warmupType
}
}

public TProperty GetPropertyAs<TProperty, TValueType>(IBindingContext context, MemberInfo memberInfo)
where TProperty : IBaseProperty
{
var property = memberInfo.GetMemberValue<IBaseProperty>(context, 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<IObjectWrapper>());
}

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<TProperty, TValueType>(IBindingContext context, BindingData bindingData,
MemberInfo memberInfo) where TProperty : IBaseProperty
{
var property = GetMemberValue<IBaseProperty>(context, memberInfo, out var propertyType);
var property = memberInfo.GetMemberValue<IBaseProperty>(context, out var propertyType);

var targetType = typeof(TValueType);
var sourceType = propertyType.GenericTypeArguments[0];
Expand Down Expand Up @@ -90,22 +139,22 @@ public TProperty GetProperty<TProperty, TValueType>(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);
}

public TCommand GetCommand<TCommand>(IBindingContext context, MemberInfo memberInfo)
where TCommand : IBaseCommand
{
return GetMemberValue<TCommand>(context, memberInfo, out _);
return memberInfo.GetMemberValue<TCommand>(context, out _);
}

public ICommandWrapper GetCommandWrapper(IBindingContext context, CommandBindingData bindingData,
MemberInfo memberInfo)
{
var command = GetMemberValue<IBaseCommand>(context, memberInfo, out var commandType);
var command = memberInfo.GetMemberValue<IBaseCommand>(context, out var commandType);

if (commandType.IsGenericType == false ||
commandType.GetInterface(nameof(IBaseCommand)) == null)
Expand Down Expand Up @@ -232,7 +281,7 @@ private void CreatePropertyValueConverterInstances(int converterId, IPropertyVal
var itemsQueue = new Queue<IObjectWrapper>();

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++)
{
Expand Down Expand Up @@ -304,32 +353,6 @@ private void ReturnWrapper(IObjectWrapper wrapper)
_wrappersByConverter[wrapper.ConverterId].Enqueue(wrapper);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static T GetMemberValue<T>(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()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Runtime.CompilerServices;

namespace UnityMvvmToolkit.Core.Internal.ObjectWrappers
{
internal sealed class PropertyCastWrapper<TSource, TValue> : PropertyWrapper<TSource, TValue>
{
[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;
}
}
}
Original file line number Diff line number Diff line change
@@ -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<TSource, TValue> : PropertyWrapper<TSource, TValue>
{
private readonly IPropertyValueConverter<TSource, TValue> _valueConverter;

[Preserve]
public PropertyConvertWrapper(IPropertyValueConverter<TSource, TValue> 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);
}
}
}
Loading