обработчики уведомлений об изменениях свойств .net - строки и выражения - PullRequest
1 голос
/ 02 ноября 2009

Использование WPF сделало меня фанатом INotifyPropertyChanged. Мне нравится использовать помощник, который принимает выражение и возвращает имя в виде строки (см. Пример кода ниже). Однако во многих приложениях, которые видят очень опытные программисты, я вижу код, который обрабатывает строки raw (см. 2-й пример ниже). Под опытным я имею в виду типы MVP, которые знают, как использовать выражения.

Мне кажется, что возможность компиляции отлавливать ошибки в дополнение к простому рефакторингу делает подход Exression лучше. Есть ли аргумент в пользу использования необработанных строк, которые мне не хватает?

Ура, Berryl

Пример помощника по выражению:

public static string GetPropertyName<T>(Expression<Func<T, object>> propertyExpression)
    {
        Check.RequireNotNull<object>(propertyExpression, "propertyExpression");
        switch (propertyExpression.Body.NodeType)
        {
            case ExpressionType.MemberAccess:
                return (propertyExpression.Body as MemberExpression).Member.Name;
            case ExpressionType.Convert:
                return ((propertyExpression.Body as UnaryExpression).Operand as MemberExpression).Member.Name;
        }
        var msg = string.Format("Expression NodeType: '{0}' does not refer to a property and is therefore not supported", 
            propertyExpression.Body.NodeType);
        Check.Require(false, msg);
        throw new InvalidOperationException(msg);
    }

Пример кода необработанных строк (в некотором классе типов ViewModelBase):

        /// <summary>
    /// Warns the developer if this object does not have
    /// a public property with the specified name. This 
    /// method does not exist in a Release build.
    /// </summary>
    [Conditional("DEBUG"), DebuggerStepThrough]

    public void VerifyPropertyName(string propertyName) {
        // Verify that the property name matches a real,  
        // public, instance property on this object.
        if (TypeDescriptor.GetProperties(this)[propertyName] == null) {
            string msg = "Invalid property name: " + propertyName;

            if (ThrowOnInvalidPropertyName) throw new Exception(msg);
            else Debug.Fail(msg);
        }
    }

    /// <summary>
    /// Returns whether an exception is thrown, or if a Debug.Fail() is used
    /// when an invalid property name is passed to the VerifyPropertyName method.
    /// The default value is false, but subclasses used by unit tests might 
    /// override this property's getter to return true.
    /// </summary>
    protected virtual bool ThrowOnInvalidPropertyName { get; private set; }

Ответы [ 5 ]

4 голосов
/ 03 ноября 2009

Для меня возможность компиляции перехватывать ошибки в дополнение к простому рефакторингу делает подход Exression лучше. Есть ли аргумент в пользу использования необработанных строк, которые мне не хватает?

Я согласен и лично использую подход выражения в моем собственном коде, в большинстве случаев.

Однако есть две причины, по которым я знаю, чтобы этого избежать:

  1. Это менее очевидно, особенно для менее опытных разработчиков .NET. Написание RaisePropertyChanged(() => this.MyProperty ); не всегда так очевидно для людей, как RaisePropertyChanged("MyProperty");, и не соответствует образцам фреймворка и т. Д.

  2. Использование выражений приводит к некоторому снижению производительности. Лично я не считаю, что это действительно имеет смысл, поскольку это обычно используется в сценариях привязки данных (которые уже медленные из-за использования отражения), но это потенциально действительная проблема.

2 голосов
/ 03 ноября 2009

Преимущество использования подхода TypeDescriptor заключается в том, что он обеспечивает динамические сценарии свойств на основе реализаций ICustomTypeDescriptor, в которых реализация может эффективно создавать метаданные динамических свойств на лету для описываемого типа. Рассмотрим DataSet, чьи «свойства» определяются, например, результирующим набором, которым он заполнен.

Это то, что выражения не предоставляют, однако, потому что оно основано на фактической информации о типе (хорошая вещь), а не на строках.

1 голос
/ 12 ноября 2009

Я потратил на это больше времени, чем ожидал, но нашел решение, в котором есть хорошее сочетание безопасности / рефакторируемости и производительности. Хорошее фоновое чтение и альтернативное решение с использованием отражения - здесь . Мне больше нравится решение Саши Барбера (фон здесь .

Идея состоит в том, чтобы использовать помощник выражений для свойства, которое будет участвовать в уведомлении об изменениях, но принимать его только один раз, сохраняя результирующий PropertyChangedEventArgs в модели представления. Например:

private static PropertyChangedEventArgs mobilePhoneNumberChangeArgs =
        ObservableHelper.CreateArgs<CustomerModel>(x => x.MobilePhoneNumber);

НТН,
Berryl

0 голосов
/ 08 июля 2013

Атрибут CallerMemberName был введен в .net 4.5 Этот атрибут может быть присоединен только к необязательным строковым параметрам, и если вызывающий не использует этот параметр при вызове функции, то имя вызывающего будет передано в строковом параметре

Это устраняет необходимость указывать имя свойства при возникновении события PropertyChanged, таким образом, оно работает с рефакторингом, и поскольку изменения выполняются во время компиляции, разницы в производительности нет.

Ниже приведен пример реализации, и дополнительную информацию можно найти в http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callermembernameattribute.aspx и http://msdn.microsoft.com/en-us/library/hh534540.aspx

    public class DemoCustomer : INotifyPropertyChanged
    {
        string _customerName
        public string CustomerName
        {
            get { return _customerNameValue;}
            set
            {
                if (value != _customerNameValue)
                {
                    _customerNameValue = value;
                    NotifyPropertyChanged();
                }
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
0 голосов
/ 26 марта 2011

Прогулка по стеку идет медленно, а лямбда-выражение еще медленнее. У нас есть решение, похожее на хорошо известное лямбда-выражение, но почти такое же быстрое, как строковый литерал. Увидеть http://zamboch.blogspot.com/2011/03/raising-property-changed-fast-and-safe.html

...