When updated to Xamarin.Forms 2.1 I got the following compilation error
_ Generic versions of Create () are no longer supported and deprecated_
Why? It looks as a step backwards.
I'm interested in knowing what the performance issue is.
For the moment I created an extension to replace the method and it looks working
internal static class BindablePropertyEx { public static BindableProperty Create<TDeclarer, TPropertyType>(Expression<Func<TDeclarer, TPropertyType>> getter, TPropertyType defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay, BindableProperty.ValidateValueDelegate<TPropertyType> validateValue = null, BindableProperty.BindingPropertyChangedDelegate<TPropertyType> propertyChanged = null, BindableProperty.BindingPropertyChangingDelegate<TPropertyType> propertyChanging = null, BindableProperty.CoerceValueDelegate<TPropertyType> coerceValue = null, BindableProperty.CreateDefaultValueDelegate<TDeclarer, TPropertyType> defaultValueCreator = null) where TDeclarer : BindableObject { return BindableProperty.Create(ExpressionEx.GetPropertyPath(getter), typeof(TPropertyType), typeof(TDeclarer), defaultValue, defaultBindingMode, validateValue: (bindable, value) => { return validateValue != null ? validateValue(bindable, (TPropertyType)value) : true; }, propertyChanged: (bindable, oldValue, newValue) => { if (propertyChanged != null) propertyChanged(bindable, (TPropertyType)oldValue, (TPropertyType)newValue); }, propertyChanging: (bindable, oldValue, newValue) => { if (propertyChanging != null) propertyChanging(bindable, (TPropertyType)oldValue, (TPropertyType)newValue); }, coerceValue: (bindable, value) => { return coerceValue != null ? coerceValue(bindable, (TPropertyType)value) : value; }, defaultValueCreator: (bindable) => { return defaultValueCreator != null ? defaultValueCreator((TDeclarer)bindable) : defaultValue; }); } }
And
internal static class ExpressionEx { public static string GetPropertyPath<T,P>(Expression<Func<T, P>> expression) { // Working outside in e.g. given p.Spouse.Name - the first node will be Name, then Spouse, then p IList<string> propertyNames = new List<string>(); var currentNode = expression.Body; while (currentNode.NodeType != ExpressionType.Parameter) { switch (currentNode.NodeType) { case ExpressionType.MemberAccess: case ExpressionType.Convert: MemberExpression memberExpression; memberExpression = (currentNode.NodeType == ExpressionType.MemberAccess ? (MemberExpression)currentNode : (MemberExpression)((UnaryExpression)currentNode).Operand); if (!(memberExpression.Member is PropertyInfo || memberExpression.Member is FieldInfo)) { throw new InvalidOperationException("The member '" + memberExpression.Member.Name + "' is not a field or property"); } propertyNames.Add(memberExpression.Member.Name); currentNode = memberExpression.Expression; break; case ExpressionType.Call: MethodCallExpression methodCallExpression = (MethodCallExpression)currentNode; if (methodCallExpression.Method.Name == "get_Item") { propertyNames.Add("[" + methodCallExpression.Arguments.First().ToString() + "]"); currentNode = methodCallExpression.Object; } else { throw new InvalidOperationException("The member '" + methodCallExpression.Method.Name + "' is a method call but a Property or Field was expected."); } break; // To include method calls, remove the exception and uncomment the following three lines: //propertyNames.Add(methodCallExpression.Method.Name); //currentExpression = methodCallExpression.Object; //break; default: throw new InvalidOperationException("The expression NodeType '" + currentNode.NodeType.ToString() + "' is not supported, expected MemberAccess, Convert, or Call."); } } return string.Join(".", propertyNames.Reverse().ToArray()); } }
Hope it helps,
Alvaro.
Hi Adam,
I see. I leave the extension I used to do the migration, there are two method, one using an expression and the other using a string, anyone wanting to do a quick migration can use the expression one, and replace the expressions by nameof() if they need more performance in iOS.
internal static class BindablePropertyEx { public static BindableProperty Create<TDeclarer, TPropertyType>(Expression<Func<TDeclarer, TPropertyType>> getter, TPropertyType defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay, BindableProperty.ValidateValueDelegate<TPropertyType> validateValue = null, BindableProperty.BindingPropertyChangedDelegate<TPropertyType> propertyChanged = null, BindableProperty.BindingPropertyChangingDelegate<TPropertyType> propertyChanging = null, BindableProperty.CoerceValueDelegate<TPropertyType> coerceValue = null, BindableProperty.CreateDefaultValueDelegate<TDeclarer, TPropertyType> defaultValueCreator = null) where TDeclarer : BindableObject { return Create<TDeclarer, TPropertyType>(ExpressionEx.GetPropertyPath(getter), defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, defaultValueCreator); } public static BindableProperty Create<TDeclarer, TPropertyType>(string propertyName, TPropertyType defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay, BindableProperty.ValidateValueDelegate<TPropertyType> validateValue = null, BindableProperty.BindingPropertyChangedDelegate<TPropertyType> propertyChanged = null, BindableProperty.BindingPropertyChangingDelegate<TPropertyType> propertyChanging = null, BindableProperty.CoerceValueDelegate<TPropertyType> coerceValue = null, BindableProperty.CreateDefaultValueDelegate<TDeclarer, TPropertyType> defaultValueCreator = null) where TDeclarer : BindableObject { BindableProperty.CreateDefaultValueDelegate dvc = null; if (defaultValueCreator != null) dvc = (bindable) => defaultValueCreator((TDeclarer)bindable); return BindableProperty.Create(propertyName, typeof(TPropertyType), typeof(TDeclarer), defaultValue, defaultBindingMode, validateValue: (bindable, value) => { return validateValue != null ? validateValue(bindable, (TPropertyType)value) : true; }, propertyChanged: (bindable, oldValue, newValue) => { if (propertyChanged != null) propertyChanged(bindable, (TPropertyType)oldValue, (TPropertyType)newValue); }, propertyChanging: (bindable, oldValue, newValue) => { if (propertyChanging != null) propertyChanging(bindable, (TPropertyType)oldValue, (TPropertyType)newValue); }, coerceValue: (bindable, value) => { return coerceValue != null ? coerceValue(bindable, (TPropertyType)value) : value; }, defaultValueCreator: dvc); } }
Thank you for your response.
Answers
We already discussed that in the release notes.
Hi Michael,
Deprecating the generic method just because the nameof function appeared doesn't look as a smart argument. Specially when the generic method has no problem, has it?
Regards,
Alvaro.
I can't find it right now, but Jason said there was a performance hit in iOS for some reason with the generic versions.
I'm interested in knowing what the performance issue is.
For the moment I created an extension to replace the method and it looks working
And
Hope it helps,
Alvaro.
Just for reference, to copy Jason's response from the referenced thread:
Hi Adam,
I see. I leave the extension I used to do the migration, there are two method, one using an expression and the other using a string, anyone wanting to do a quick migration can use the expression one, and replace the expressions by nameof() if they need more performance in iOS.
Thank you for your response.
Deprecated .
New version:
Deprecated .
New version: