INotifyPropertyChanged의 실현에 대해 이야기하다

5183 단어 property
INotifyPropertyChanged 인터페이스는 WPF/Silverlight 개발에서 매우 중요한 인터페이스로 ViewModel의 기초를 구성하고 데이터 귀속은 기본적으로 이 인터페이스가 필요하다.그래서 그 실현도 매우 중요하다. 다음에 내가 아는 몇 가지 실현 방식을 붙여서 벽돌을 던져 옥을 끌어올리는 역할을 하고 싶다.
일반적인 실현 방식
이것은 다음과 같은 일반적인 실현 방식이다.
public class NotifyPropertyChanged : INotifyPropertyChanged {
   
   public event PropertyChangedEventHandler PropertyChanged;

   virtual internal protected void OnPropertyChanged(string propertyName) {
      if (this.PropertyChanged != null) {
         this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
      }
   }
}

이러한 방식을 일반적인 실현 방식이라고 부른다. 왜냐하면 이것은 정말 너무 일반적이기 때문에 사용하기에도 혐오감을 느낀다. 왜냐하면 수동으로 속성 이름을 지정해야 하기 때문이다.
public class MyViewModel : NotifyPropertyChanged {

   private int _myField;

   public int MyProperty {
      get { return _myField; }
      set {
         _myField = value;
         OnPropertyChanged("MyProperty");
      }
   }
}

lambda 표현식 실현 방식
lambda 표현식에 익숙한 학생들은 lambda 표현식으로 속성 이름 전달을 실현하고 NotifyPropertyChanged 클래스에 이런 방법을 추가하는 것을 고려할 수 있다.
protected void SetProperty<T>(ref T propField, T value, Expression<Func<T>> expr) {
   var bodyExpr = expr.Body as System.Linq.Expressions.MemberExpression;
   if (bodyExpr == null) {
      throw new ArgumentException("Expression must be a MemberExpression!", "expr");
   }
   var propInfo = bodyExpr.Member as PropertyInfo;
   if (propInfo == null) {
      throw new ArgumentException("Expression must be a PropertyExpression!", "expr");
   }
   var propName = propInfo.Name;
   propField = value;
   this.OnPropertyChanged(propName);
}

Notify Property Changed 기본 클래스를 사용하면 다음과 같은 이점을 얻을 수 있습니다.
public class MyViewModel : NotifyPropertyChanged {

   private int _myField;

   public int MyProperty {
      get { return _myField; }
      set {
         base.SetProperty(ref _myField, value, () => this.MyProperty);
          }
   }
}

이렇게 되면 속성 명칭을 문자열로 전달하는 것을 lambda 표현식으로 전달하는 것으로 바꾸어 하드코딩을 줄였기 때문에 확실히 많이 편리해졌지만 그래도 약간 번거로움을 느꼈다. 그래도 lambda 표현식을 써서 속성 명칭을 전달해야 한다.
차단 방식 실현
Castal에 대해DynamicProxy가 인상적이라면 DynamicProxy를 사용하여 차단하는 것을 고려할 수 있습니다. 제 실현은 다음과 같습니다.
// 1.         ,    PostProcess   ,         set_       ,
//             ,             。
internal class NotifyPropertyChangedInterceptor : StandardInterceptor {

   protected override void PostProceed(IInvocation invocation) {
      base.PostProceed(invocation);
      var methodName = invocation.Method.Name;
      if (methodName.StartsWith("set_")) {
         var propertyName = methodName.Substring(4);
         var target = invocation.Proxy as NotifyPropertyChanged;
         if (target != null) {
            target.OnPropertyChanged(propertyName);
         }
      }
   }
}

// 2.         ,              。
public static class ViewModelHelper {

   private static readonly ProxyGenerator ProxyGenerator = new ProxyGenerator();
   private static readonly NotifyPropertyChangedInterceptor Interceptor
         = new NotifyPropertyChangedInterceptor();

   public static T CreateProxy<T>(T obj) where T : class, INotifyPropertyChanged {
      return ProxyGenerator.CreateClassProxyWithTarget(obj, Interceptor);
   }
}

사용하기 쉽지만 ViewModel 객체를 만들 때는 다음과 같은 도움말 클래스를 사용하여 인스턴스를 만들어야 합니다.
public class MyViewModel : NotifyPropertyChanged {

   //               ,            。
   public int MyProperty {
      get; set;
   }
}
//            :
var viewModel = ViewModelHelper.CreateProxy<MyViewModel>();
viewModel.MyProperty = 100;

그러나 이러한 실현의 단점은 모든 속성이PropertyChanged 이벤트를 촉발할 수 있고 하나의 이벤트만 촉발할 수 있다는 것이다. 실제 개발에서 간혹 하나의 속성을 설정하여 여러 개의PropertyChanged 이벤트를 촉발해야 한다.
미래.Net 4.5 구현 방법
곧 발표될 예정입니다.Net 4.5는 4CallerMemberNameAttribute태그, 이 속성을 이용하여 위에서 제공한 SetProperty 방법을 개조할 수 있습니다. 이런 실현이야말로 가장 완벽한 것입니다.
protected void SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null) {
   if (object.Equals(storage, value)) return;

   storage = value;
   this.OnPropertyChanged(propertyName);
}

Caller MemberName 태그 지원이 있으므로 사용하기 편리합니다.
public class MyViewModel : NotifyPropertyChanged {

   private int _myField;

   public int MyProperty {
      get { return _myField; }
      set {
         base.SetProperty(ref _myField, value);
      }
   }
}

이런 방법은 비록 좋지만, 오직 있을 뿐이다.Net 4.5에는 없으며 Silverlight에 추가되지 않을 수도 있습니다.

좋은 웹페이지 즐겨찾기