Реализация INotifyPropertyChanged - существует ли лучший способ?

Надеемся, что это поможет:

public static int countOfStringInAText(String stringToBeSearched, String masterString){

    int count = 0;
    while (masterString.indexOf(stringToBeSearched)>=0){
      count = count + 1;
      masterString = masterString.substring(masterString.indexOf(stringToBeSearched)+1);
    }
    return count;
}
608
задан 3 revs, 3 users 75% 9 April 2013 в 13:04
поделиться

6 ответов

Без использования чего-то вроде postharp в минимальной версии, которую я использую, используется что-то вроде:

public class Data : INotifyPropertyChanged
{
    // boiler-plate
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    protected bool SetField<T>(ref T field, T value, string propertyName)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    // props
    private string name;
    public string Name
    {
        get { return name; }
        set { SetField(ref name, value, "Name"); }
    }
}

Каждое свойство в таком случае выглядит примерно так:

    private string name;
    public string Name
    {
        get { return name; }
        set { SetField(ref name, value, "Name"); }
    }

что не очень много; при желании его также можно использовать в качестве базового класса. bool , возвращаемый из SetField , сообщает вам, было ли это отключением, если вы хотите применить другую логику.


или даже проще с C # 5:

protected bool SetField<T>(ref T field, T value,
    [CallerMemberName] string propertyName = null)
{...}

, который можно вызвать так:

set { SetField(ref name, value); }

, с которым компилятор автоматически добавит «Имя» .


C # 6.0 упрощает реализацию:

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

... а теперь с C # 7:

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
   => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));


private string name;
public string Name
{
    get => name;
    set => SetField(ref name, value);
}
611
ответ дан 22 November 2019 в 21:55
поделиться

Мне очень нравится решение Марка, но я думаю, что его можно немного улучшить, чтобы избежать использования «волшебной строки» (которая не поддерживает рефакторинг). Вместо использования имени свойства в виде строки его легко сделать лямбда-выражением:

private string name;
public string Name
{
    get { return name; }
    set { SetField(ref name, value, () => Name); }
}

Просто добавьте следующие методы в код Марка, и это поможет:

protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
{
    if (selectorExpression == null)
        throw new ArgumentNullException("selectorExpression");
    MemberExpression body = selectorExpression.Body as MemberExpression;
    if (body == null)
        throw new ArgumentException("The body must be a member expression");
    OnPropertyChanged(body.Member.Name);
}

protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression)
{
    if (EqualityComparer<T>.Default.Equals(field, value)) return false;
    field = value;
    OnPropertyChanged(selectorExpression);
    return true;
}

Кстати, это было вдохновлено this сообщение в блоге обновленный URL-адрес

162
ответ дан 22 November 2019 в 21:55
поделиться

На самом деле у меня еще не было возможности попробовать это сам, но в следующий раз, когда я создаю проект с большими требованиями для INotifyPropertyChanged, я собираюсь написать Атрибут Postsharp , который будет внедрять код во время компиляции. Что-то вроде:

[NotifiesChange]
public string FirstName { get; set; }

Превратится в:

private string _firstName;

public string FirstName
{
   get { return _firstname; }
   set
   {
      if (_firstname != value)
      {
          _firstname = value;
          OnPropertyChanged("FirstName")
      }
   }
}

Я не уверен, сработает ли это на практике, и мне нужно сесть и попробовать, но я не понимаю, почему бы и нет. Мне может потребоваться заставить его принимать некоторые параметры в ситуациях, когда необходимо активировать более одного OnPropertyChanged (если, например, у меня было свойство FullName в указанном выше классе)

В настоящее время я использую настраиваемый шаблон в Resharper, но даже с этим я устаю от того, что все мои владения такие длинные.


Ах, быстрый поиск в Google (который я должен был сделать до того, как написал это) показывает, что по крайней мере один человек делал что-то подобное до здесь . Не совсем то, что я имел в виду, но достаточно близко, чтобы показать, что теория верна.

15
ответ дан 22 November 2019 в 21:55
поделиться

Подход, очень похожий на АОП, заключается во внедрении материала INotifyPropertyChanged в уже созданный объект на лету. Вы можете сделать это с помощью чего-то вроде Castle DynamicProxy. Вот статья, объясняющая этот метод:

Добавление INotifyPropertyChanged к существующему объекту

7
ответ дан 22 November 2019 в 21:55
поделиться

Другие вещи, которые вы можете принять во внимание при реализации такого рода свойств, - это тот факт, что оба INotifyPropertyChang * ed *ing используют классы аргументов событий.

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

Взгляните на это реализация и объяснение, почему это было задумано.

Блог Джоша Смита

1
ответ дан 22 November 2019 в 21:55
поделиться

Поговорим о чрезмерной инженерии. Это значительно сложнее, чем , просто делать это правильно и дает мало пользы или вообще не дает. Если ваша IDE поддерживает фрагменты кода (Visual Studio / MonoDevelop do), вы можете сделать это до смешного простым. Все, что вам нужно будет ввести, - это тип свойства и имя свойства. Дополнительные три строки кода будут созданы автоматически.

-3
ответ дан 22 November 2019 в 21:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: