locked
Generic versions of Create () are no longer supported and deprecated RRS feed

  • Question

  • User84937 posted

    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.

    Friday, March 18, 2016 2:59 PM

Answers

  • User84937 posted

    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.

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Friday, March 18, 2016 7:40 PM
  • User84937 posted

    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.

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Monday, March 21, 2016 4:10 PM

All replies

  • User57869 posted

    We already discussed that in the release notes.

    Friday, March 18, 2016 3:08 PM
  • User84937 posted

    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.

    Friday, March 18, 2016 3:22 PM
  • User129559 posted

    I can't find it right now, but Jason said there was a performance hit in iOS for some reason with the generic versions.

    Friday, March 18, 2016 4:16 PM
  • User84937 posted

    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.

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Friday, March 18, 2016 7:40 PM
  • User129559 posted

    Just for reference, to copy Jason's response from the referenced thread:

    yeah we don't like it either, and if you want to use the expression API go ahead, we wont be removing it in the 2.0 series at all, but be aware that there is nothing we can do about the iOS bloat due to AOT requirement. Without a JIT expression is extremely bloated and slow.

    Friday, March 18, 2016 7:59 PM
  • User84937 posted

    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.

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Monday, March 21, 2016 4:10 PM
  • User305535 posted

    Deprecated .

       public static readonly BindableProperty ProgressValueProperty =
            BindableProperty.Create<MyControl, double>
       (p => p.Value, double.NaN, BindingMode.TwoWay, propertyChanged: (bindable, oldValue, newValue) =>
       {
           var control = (MyControl)bindable;
           control.Value = newValue;
       });
    

    New version:

     public static readonly  BindableProperty ProgressValueProperty = BindableProperty.Create("Value", typeof(double), 
     typeof(MyControl), null, BindingMode.TwoWay, null, propertyChanged: OnItemsSourceChanged);
      static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var control = (MyControl)bindable;
            control.Value = (double)newValue;
        }
    
    Friday, April 13, 2018 5:58 PM
  • User305535 posted

    Deprecated .

           public static readonly BindableProperty ProgressValueProperty =
                BindableProperty.Create<MyControl, double>
           (p => p.Value, double.NaN, BindingMode.TwoWay, propertyChanged: (bindable, oldValue, newValue) =>
           {
               var control = (MyControl)bindable;
               control.Value = newValue;
           });
    

    New version:

     public static readonly  BindableProperty ProgressValueProperty = BindableProperty.Create("Value", typeof(double), 
     typeof(MyControl), double.NaN, BindingMode.TwoWay, null, propertyChanged: OnItemsSourceChanged);
      static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var control = (MyControl)bindable;
            control.Value = (double)newValue;
        }
    
    Sunday, April 15, 2018 7:19 AM