Существует ли хороший способ со строгим контролем типов сделать события PropertyChanged в C#?

Интерфейсы определяют поведение объекта.

Статические методы не определяют поведение объекта, но поведение, которое влияет на объект в некотором роде.

10
задан Davy8 14 July 2009 в 21:16
поделиться

6 ответов

Правка: nameof прибыл в C # 6. Ура!


Нет nameof / информации о и т. Д .; это много обсуждается, но это то, что есть.

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


using System;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;
class Program : INotifyPropertyChanged {
    public event PropertyChangedEventHandler PropertyChanged;
    static void Main() {
        var p = new Program();
        p.PropertyChanged += (s, a) => Console.WriteLine(a.PropertyName);
        p.Name = "abc";
    }
    protected void OnPropertyChanged<T>(Expression<Func<Program, T>> property) {
        MemberExpression me = property.Body as MemberExpression;
        if (me == null || me.Expression != property.Parameters[0]
              || me.Member.MemberType != MemberTypes.Property) {
            throw new InvalidOperationException(
                "Now tell me about the property");
        }
        var handler = PropertyChanged;
        if (handler != null) handler(this,
          new PropertyChangedEventArgs(me.Member.Name));
    }
    string name;
    public string Name {
        get{return name;}
        set {
            name = value;
            OnPropertyChanged(p=>p.Name);
        }
    }
}
8
ответ дан 3 December 2019 в 22:01
поделиться

Простейшим решением является просмотр трассировки стека и полное удаление каждой явной ссылки на свойство.

public String Name
{
    get { return this.name; }
    set
    {
        if (value != this.name)
        {
            this.RaisePropertyChanging();
            this.name = value;
            this.RaisePropertyChanged();
        }
    }
}
private String name = null;

private void RaisePropertyChanged()
{
    String propertyName =
       new StackTrace().GetFrame(1).GetMethod().Name.SubString(4);

    PropertyChangedEventHandler handler = this.PropertyChanged;
    if (handler != null)
    {
        handler(new PropertyChangedEventArgs(propertyName));
    }
}

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

Другое решение - получить имя свойства из лямбда-выражения.

public static String GetPropertyNameFromLambdaExpression<TObject, TProperty>(
    Expression<Func<TObject, TProperty>> expression)
{
    return ((MemberExpression)expression.Body).Member.Name;
}

Например,

GetPropertyNameFromLambdaExpression<String, Int32>(s => s.Length)

вернет « Длина » как и ожидалось. Производственная версия кода действительно требует дополнительных проверок и лучшей интеграции с остальной частью кода. Например, можно использовать вывод типа для общих аргументов.

UPDATE

И есть третье решение - вы можете использовать MethodBase. GetCurrentMethod () внутри средства получения или установки свойства для получения имени метода установки или получения.

public String Name
{
    get { return this.name; }
    set
    {
        if (value != this.name)
        {
            String propertyName = MethodBase.GetCurentMethod().Name.SubString(4);

            this.RaisePropertyChanging(propertyName);
            this.name = value;
            this.RaisePropertyChanged(propertyName);
        }
    }
}
private String name = null;
2
ответ дан 3 December 2019 в 22:01
поделиться

Не ответ на ваш вопрос, но если вы щелкните правой кнопкой мыши-> Рефакторинг-> Переименовать свойство, оно также может переименовать совпадающие строки, включая любые строки, соответствующие имени вашего свойства.

Да, это может быть немного опасно.

0
ответ дан 3 December 2019 в 22:01
поделиться

Теоретически вы можете использовать MethodBase.GetCurrentMethod (). Name.Substring (4) из установщика свойств. К сожалению, поиск Google показывает, что он, похоже, оказывает значительное влияние на производительность . Еще две вещи, которые следует учитывать:

  • JIT-встраивание может повлиять на это неожиданным образом. (stackoverflow.com/questions/616779/can-i-check-if-the-c-compiler-in Line-a-method-call)
  • Теоретически вызов IL метода MethodBase.GetCurrentMethod () можно тривиально заменить JIT во время выполнения с помощью инструкции ldtoken, за которой следует вызов MethodBase.GetMethodFromHandle (), что было бы очень быстро. Думаю, пользователи просто не выразили потребности в этом. (msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.ldtoken.aspx)
  • Полностью мое мнение здесь, но я думаю, что это ' Было бы неплохо иметь операторы fieldof () и methodof () в C #. Я считаю, что это значительно повысило бы надежность инструментов анализа / рефакторинга кода в проектах, которые требуют такой способности.
1
ответ дан 3 December 2019 в 22:01
поделиться

PropertyChangedEventArgs принимает только один конструктор, которому требуется имя свойства в виде строки. Таким образом, отсутствие использования INotifyPropertyChanged означает, что на каком-то уровне, будь то высокий или низкий в вашей архитектуре, вам придется работать со строкой и переименованием вручную.

-1
ответ дан 3 December 2019 в 22:01
поделиться

Вы должны проверить это сообщение в блоге . Это дает вам возможность сделать следующее:

string propertyName = TypeHelper.GetPropertyName<User>(u => u.LastProjectCode);

PropertyInfo property1 = TypeHelper.GetProperty((SomeClass o) => o.InstanceProperty.Length);

PropertyInfo property2 = TypeHelper.GetProperty(() => SomeClass.StaticProperty.Length);

Переименование в Visual Studio / Resharper / Refactor Pro должно работать на вас.

0
ответ дан 3 December 2019 в 22:01
поделиться
Другие вопросы по тегам:

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