13. May 2012 · 4 comments · Categories: .NET 4.5, C#

Новый C# принес всего три новые фичи. Основаная – async/await уже достаточно разрекламирована. Вторая – изменение области видимости переменной цикла в foreach – скорее исправление ошибки в дизайне языка, чем полноценная фича. Третье изменение, которое затронуло непосредственно сам язык – это появление аттрибутов Caller Info.

Аттрибуты Caller Info – это три аттрибута: CallerFilePathAttribute, CallerLineNumberAttribute и CallerMemberNameAttribute. Их можно применить только к необязательным параметрам. Встретив вызов метода с одим из этих аттрибутов, компилятор попытается подставить информацию о вызывающем методе вместо значения параметра по умолчанию.

Есть два прямых применения этих аттрибутов. Первое – помощь в логгировании и трассировке. В примерах MSDN показано именно применение Caller Info вместе с System.Diagnostics.Trace:

Второе, и, наверное, более интересное примение – это упрощение реализации интерфейса INotifyPropertyChanged.

Стандартная реализация INotifyPropertyChanged в C# 4.0 выглядит так:

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }

    public string CustomerName
    {
        get
        {
            return this.customerNameValue;
        }

        set
        {
            if (value != this.customerNameValue)
            {
                this.customerNameValue = value;
                NotifyPropertyChanged("CustomerName");
            }
        }
    }

В C# 5.0 она упрощается до:

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public string CustomerName
    {
        get
        {
            return this.customerNameValue;
        }

        set
        {
            if (value != this.customerNameValue)
            {
                this.customerNameValue = value;
                NotifyPropertyChanged();
            }
        }
    }   

Без явного упоминания свойства по имени шанс поломать что-то при рефакторинге немного сокращается.

Да, ReSharper и раньше старался менять строковые константы при переименованиях свойств. И было несколько решений с лямбда-выражениями, вида NotifyPropertyChanged(() => this.CustomerName). Но встроенное в язык решение все-таки приятнее, чем обходные пути.