2010-07-31 40 views
1

這個問題可能會需要一段時間來解釋的對象實例,我需要提供背景...獲取其中包含了指定的屬性

這只是我打得關於與和ISN」噸產品,但此刻我有一些代碼,看起來像這樣:

var myDataModel = new DataModel(); 

myDataModel.PropertyChanged += myDataModel_PropertyChanged; 

myDataModel.ChangeProperty(t => t.TestValue, 2); 

所以,而不是直接使用myDataModel.TestValue = 2,我使用的是ChangeProperty擴展方法,這樣我可以處理所有的變化事件並在一個地方做任何我想做的事情。我的擴展方法看起來是這樣的(是的,我知道這是哈克):

public static class NotifyPropertyChangedExtensions 
{ 
    public static void ChangeProperty<T, U>(
             this T instance, 
             Expression<Func<T, U>> propertyToChange, 
             U newValue) 
    { 
     var member = propertyToChange.Body as MemberExpression; 

     if (member != null) 
     { 
      if (!propertyToChange.Compile().Invoke(instance).Equals(newValue)) 
      { 
       var setProperty = instance.GetType().GetProperty(
             member.Member.Name, 
             BindingFlags.SetProperty | 
             BindingFlags.Public | 
             BindingFlags.Instance); 

       if (setProperty != null) 
       { 
        // actually set the property 
        setProperty.SetValue(instance, newValue, null); 

        // raise the property changed event 
        if (typeof(INotifyPropertyChanged).IsAssignableFrom(
                   typeof(T))) 
        { 
         var delegatesToCall = 
          instance.GetType().GetField("PropertyChanged", 
             BindingFlags.Instance | 
             BindingFlags.NonPublic) 
            .GetValue(instance) as MulticastDelegate; 


         if (delegatesToCall != null) 
         { 
          var eventArgs = new PropertyChangedEventArgs(
                  setProperty.Name); 
          foreach (var @delegate in 
             delegatesToCall.GetInvocationList()) 
          { 
           @delegate.Method.Invoke(
             @delegate.Target, 
             new object[] { instance, eventArgs }); 
          } 
         } 

        } 
       } 
      } 
     } 
     else 
     { 
      throw new ArgumentException(
        string.Format(
          "Cannot determine the property to change {0}", 
          propertyToChange)); 
     } 
    } 
} 

在此架構下,我的數據模型是相當乾淨:

public class DataModel : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    public int TestValue { get; set; } 
} 

也就是說,我可以使用自動屬性,而不必擔心引發事件等

現在,我真正想要做的是更接近於這樣的:

var dataModel = new DataModel(); 

myDataModel.PropertyChanged += myDataModel_PropertyChanged; 

myDataModel.TestValue.Set(2); // this is what I want... 

所以,我想我基本上需要一個擴展方法 - 但我只能看到如何發送屬性本身(在這種情況下是TestValue)和新的值。那麼我想知道給定一個屬性是否有可能找到它所屬類的實例?

+0

Tbh我不會這樣做。您正在推動將PropertyChanged事件從類中正確提升到其用戶的責任。這打破了封裝原則。該類應確保它的行爲與預期相同,而不是使用它的代碼。 – dtb 2010-07-31 13:39:37

+0

@dtb:這是一個「是否有可能」的問題,我不會把這個附近的生產代碼... – 2010-07-31 13:44:49

+0

而不是'var setProperty = instance.GetType()。GetProperty(...)',它依賴於財產*名稱*,您應該可以從propertyToChange獲得財產。例如,在你的代碼中,'(PropertyInfo)member.Member'就可以做到。 – Timwi 2010-07-31 14:13:53

回答

1
  1. 不要這樣做。它打破封裝。

  2. 否。在myDataModel.TestValue.Set(2);擴展方法將始終對該屬性返回的值調用。沒有辦法獲得返回值的類,實例或屬性。

  3. 你可以做這樣的事情:

    var t = new DataModel(); 
    ((Expression<Func<int>>)(() => t.Foo)).Set(100); 
    

    static class Extensions 
    { 
        public static void Set<T>(this Expression<Func<T>> expression, T value) 
        { ... } 
    } 
    

    但是這是醜陋的,幾乎是不可讀的,不清,效率低下,而且容易出錯。

  4. 您正在尋找面向方面編程(Aspect Oriented Programming,AOP)。

    看看PostSharp或LinFu。

  5. 還沒有真正乾淨的解決方案來實現INotifyPropertyChanged呢。如果鍵入所有的屬性設置器太多工作或太容易出錯,我會用T4模板生成它們。

相關問題