2012-07-12 66 views
2

我現有的類結構。使用泛型基本抽象類方法訪問子類對象

AbstractTradeBaseClass - > AbstractTradeClass - > ConcreteTradeClass。

我使用DataAnnotations和IDataErrorInfo來驗證我的模型在我的WPF應用程序。 我已將IDataErrorInfo方法移至AbstractTradeBaseClass,以便我可以使用所有從基類繼承的類。

這是通過使用Linq動態讀取屬性來完成的。

AbstractTradeBaseClass.cs

public abstract class TradeBaseModel<T> : INotifyPropertyChanged, IDataErrorInfo 
{ 
    #region INotifyPropertyChanged Members 

    public event PropertyChangedEventHandler PropertyChanged; 

    public void OnPropertyChanged(string propertyName) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 

    #endregion 

    # region Getter/Validator members for Annotations 
    private static readonly Dictionary<string, Func<T, object>> 
     propertyGetters = typeof(T).GetProperties() 
          .Where(p => GetValidations(p).Length != 0) 
          .ToDictionary(p => p.Name, p => GetValueGetter(p)); 

    private static readonly Dictionary<string, ValidationAttribute[]> validators = 
     typeof(T).GetProperties() 
     .Where(p => GetValidations(p).Length != 0) 
     .ToDictionary(p => p.Name, p => GetValidations(p)); 

    private static ValidationAttribute[] GetValidations(PropertyInfo property) 
    { 
     return (ValidationAttribute[])property.GetCustomAttributes(typeof(ValidationAttribute), true); 
    } 

    private static Func<T, object> GetValueGetter(PropertyInfo property) 
    { 
     var instance = Expression.Parameter(typeof(T), "i"); 
     var cast = Expression.TypeAs(Expression.Property(instance, property), typeof(object)); 
     return (Func<T, object>)Expression.Lambda(cast, instance).Compile(); 
    } 
    # endregion 

    #region IDataErrorInfo Members 

    public string Error 
    { 
     get 
     { 
      var errors = from i in validators 
         from v in i.Value 
         where !v.IsValid(propertyGetters[i.Key](**(T)ModelToValidate()**)) 
         select v.ErrorMessage; 
      return string.Join(Environment.NewLine, errors.ToArray()); 
     } 
    } 

    protected Dictionary<string, string> _errors = new Dictionary<string, string>(); 
    public IDictionary<string, string> Errors 
    { 
     get { return _errors; } 
    } 

    public string this[string columnName] 
    { 
     get 
     { 
      string errorMessage = string.Empty; 
      this.Errors.Remove(columnName); 

      //string errorMessage = string.Empty; 
      //switch(columnName) 
      //{ 
      // case "Range": 
      //  if (Range > 15) 
      //  { 
      //   errorMessage = "Out of Range, should be less than 15"; 
      //  } 
      //  break; 
      //} 

      //return errorMessage; 

      if (propertyGetters.ContainsKey(columnName)) 
      { 
       var value = propertyGetters[columnName](**(T)ModelToValidate()**); 
       var errors = validators[columnName].Where(v => !v.IsValid(value)) 
        .Select(v => v.ErrorMessage).ToArray(); 

       string error = string.Join(Environment.NewLine, errors); 
       this.OnPropertyChanged("Error"); 

       if (!string.IsNullOrEmpty(error)) 
       { 
        this.Errors.Add(columnName, error); 
       } 

       return error; 
      } 

      return string.Empty; 

     } 
    } 

    public abstract object ModelToValidate(); -- ***Gets the child object here, overridden in the child class to return the child object, -- is there a better way to do this using Generics ??*** 

    #endregion 
} 

AbstractTradeClass

public abstract class TradeClassBase<T> : TradeBaseModel<T> 
{ 
    public string EmptyString 
    { 
     get { return string.Empty; } 
    } 
} 

ConcreteTradeClass

public class CashFlowTrade : TradeClassBase<CashFlowTrade> 
{ 
    private int range; 
    [RangeAttribute(0,10, ErrorMessage="Out of Range, Range should be less than 15")] 
    public int Range 
    { 
     get 
     { 
      return this.range; 
     } 
     set 
     { 
      if (this.range != value) 
      { 
       this.range = value; 
       this.OnPropertyChanged("Range"); 
      } 
     } 
    } 

    public override object ModelToValidate() 
    { 
     return this; 
    } 

} 

是否有更好的方法來做到這一點,而不是使用抽象方法並在子類中重寫它以傳遞實際的子類對象並將其轉換爲類型T. 以上代碼以BOLD表示。

如果有方法可以將子類對象傳遞給基類,那麼我可以使用實際的子類對象本身來執行驗證操作。


更新工作守則 約束T,帶類,並使用類型轉換操作如下。

public abstract class TradeBaseModel<T> : INotifyPropertyChanged, IDataErrorInfo 
    where T : class 

替換呼叫到與鑄型的抽象方法。

if (propertyGetters.ContainsKey(columnName)) 
      { 
       var value = propertyGetters[columnName](this as T); 

回答

2

是......直接使用這個。即使在抽象類中,您也可以訪問此引用。唯一不能使用它的地方是靜態方法。

編輯:

要強制類型檢查,你可以對T添加約束條件:

public abstract class TradeBaseModel<T> : INotifyPropertyChanged, IDataErrorInfo where T: TradeBaseModel<T> 

編輯2:

綜上所述向上各Kans '下面的評論,這是不夠的:這種轉換啓用隱式類型轉換fr om T到基本類型,而需要從基本類型到T的轉換。 唯一的解決方案似乎是在上面的代碼中使用T作爲運算符,爲此,T必須是一個類(如果添加上面的約束,那麼就可以)。

+0

非常棒的KeK,你讓我在正確的方向思考。 我試過(這個),但是有一個類型不匹配的錯誤,因爲T是一個派生類型,這將涉及基類型,所以VS也不喜歡它,所以這次採取了不同的方法。 將基類和派生類約束到Where T:類,然後使用as運算符對此進行類型化,這似乎工作。 所以主要是什麼不同之間的(T)這和(這是T),前者給出了一個錯誤,不能被施放,但後來的作品,我猜這一切都歸結爲協方差,原則.. – Kans 2012-07-12 12:27:03

+0

行...我想我並不完全理解這個含義。請參閱經過編輯的答案,解決方法是避免as運算符 – Kek 2012-07-12 12:57:59

+0

這不起作用,因爲T是派生類型,因爲它在字典中 - Func ,當執行此操作的(T)時,轉換無效,所以仍然會遇到類型不匹配時使用此約束T到我的基類型後... – Kans 2012-07-12 13:13:25