Добавлен ответ C # 6
В C # 6 (и любой версии VB, поставляемой с Visual Studio 2015) у нас есть оператор nameof
, который делает вещи проще, чем когда-либо. В моем исходном ответе ниже я использую функцию C # 5 (атрибуты информации о вызывающем абоненте) для обработки общего случая «самоизменяющихся» уведомлений. Оператор nameof
может использоваться во всех случаях, и он особенно полезен в сценарии уведомления «related-property-change».
Для простоты, я думаю, я сохраню подход атрибута информации о вызывающем абоненте для общих самоизменяющихся уведомлений. Меньшее количество операций ввода означает меньше шансов на опечатки и ошибки, вызванные копированием / вставкой ... компилятор здесь гарантирует, что вы выбрали правильный тип / член / переменную, но не гарантирует, что вы выбрали правильный. Затем просто использовать новый оператор nameof
для уведомлений об изменениях связанных свойств. В приведенном ниже примере демонстрируется ключевое поведение атрибутов информации о вызывающем абоненте ... атрибут не влияет на параметр, если параметр указан вызывающим абонентом (то есть информация о вызывающем абоненте предоставляется для значения параметра, только если параметр опущен вызывающая сторона).
Стоит также отметить, что оператор nameof
также может использоваться обработчиками событий PropertyChanged. Теперь вы можете сравнить значение PropertyName
в событии (которое является string
) с определенным свойством, используя оператор nameof
, исключая больше волшебных строк.
Справочная информация для nameof
здесь: https://msdn.microsoft.com/en-us/library/dn986596.aspx
Пример:
public class Program
{
void Main()
{
var dm = new DataModel();
dm.PropertyChanged += propertyChangedHandler;
}
void propertyChangedHandler(object sender, PropertyChangedEventArgs args)
{
if (args.PropertyName == nameof(DataModel.NumberSquared))
{
//do something spectacular
}
}
}
public class DataModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
public class DataModel : DataModelBase
{
//a simple property
string _something;
public string Something
{
get { return _something; }
set { _something = value; OnPropertyChanged(); }
}
//a property with another related property
int _number;
public int Number
{
get { return _number; }
set
{
_number = value;
OnPropertyChanged();
OnPropertyChanged(nameof(this.NumberSquared));
}
}
//a related property
public int NumberSquared { get { return Number * Number; } }
}
Оригинальный ответ C # 5
Начиная с C # 5, лучше всего использовать атрибуты информации о вызывающем абоненте, это разрешается во время компиляции, не требуется никакого отражения.
Я реализую это в базовом классе, производные классы просто вызывают метод OnPropertyChanged
из своих установщиков свойств. Если какое-то свойство неявно меняет другое значение, я могу также использовать «явную» версию метода в установщике свойств, которая затем перестает быть «безопасной», но это редкая ситуация, которую я просто принимаю.
В качестве альтернативы вы можете использовать этот метод для уведомлений об изменении себя и использовать ответ, данный @Jehof для уведомлений об изменениях связанных свойств ... это будет иметь преимущество в том, что у вас не будет волшебных строк, а самое быстрое выполнение для общего случая self изменить уведомления.
Это последнее предложение реализовано ниже (думаю, я начну его использовать!)
public class DataModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
OnPropertyChangedExplicit(propertyName);
}
protected void OnPropertyChanged<TProperty>(Expression<Func<TProperty>> projection)
{
var memberExpression = (MemberExpression)projection.Body;
OnPropertyChangedExplicit(memberExpression.Member.Name);
}
void OnPropertyChangedExplicit(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
public class DataModel : DataModelBase
{
//a simple property
string _something;
public string Something
{
get { return _something; }
set { _something = value; OnPropertyChanged(); }
}
//a property with another related property
int _number;
public int Number
{
get { return _number; }
set
{
_number = value;
OnPropertyChanged();
OnPropertyChanged(() => NumberSquared);
}
}
//a related property
public int NumberSquared { get { return Number * Number; } }
}