2010-02-26 61 views
2

所以我有下面的代碼片段:如何覆蓋計算的值?

private Nullable<decimal> _excessWages; 
public decimal ExcessWages 
{ 
    get 
    { 
     return _excessWages ?? CalculateExcessWages(); 
    } 
    set 
    { 
     if (value != CalculateExcessWages()) 
      _excessWages = value; 
     else 
      _excessWages = null; 
    } 
} 

所以基本上我想要實現的行爲是,如果一個字段爲空或指定的值等於計算出一個使用的計算值,否則存儲分配的值。

我有很多需要支持像這樣覆蓋的字段。這是完成這個最好的方法嗎?如果不是,你會建議什麼?

+0

你究竟在問什麼?我發現這個屬性被聲明爲'virtual',但是從文本中我知道你更關心setter中的代碼構造了嗎? – 2010-02-26 16:14:36

+0

對不起。我刪除了虛擬。這不是我所感興趣的,我關心的是從最終用戶的角度來看'可重複'。 – 2010-02-26 16:16:41

回答

2

我曾在此基礎大多Vlad的建議了一下。原來你可以用一個泛型類來抽象這個。下面是最終的結果:

public class Overridable<T> 
{ 
    private Func<T> _calculate; 
    private readonly Func<T, T, bool> _compare; 
    protected T _t; 

    public Overridable(Func<T> calculate, Func<T, T, bool> compare) 
    { 
     _calculate = calculate; 
     _compare = compare; 
    } 

    public T Value 
    { 
     get { return _compare(_t, default(T)) ? _calculate() : _t; } 
     set { _t = _compare(value, _calculate()) ? default(T) : value; } 
    } 
} 

你需要在一個比較委託,直到你在子類中設置爲通過,因爲該類型是不知道。所以一個簡單的==不會削減它。我走了簡單的路線,並使用了Func委託,但如果由於某種原因必須適應.NET 2.0,則可以使用普通委託來替換它。

您會注意到我使用的是default(T)而不是null。這是有效的,因爲Nullable<T>的默認值是null(或者更確切地說,未定義,但它看起來是相同的)。

這並不妨礙您嘗試爲非空類型聲明Overridable<T>。你最終得到的結果將不會通過運行時錯誤,但它不是有用的。試圖設置一個Overridable<decimal>.Valuenull會給你一個編譯器錯誤。將其設置爲default(decimal)將導致它恢復爲計算該值。

我走了這條路線,因爲我使用這個類的屬性需要填充一個可序列化的對象,它最終以xml的形式傳輸。 xml的模式包括定義爲整數,小數和字符串混合的數字字段。

然後,您使用Overriddable類,像這樣:

private Overridable<decimal?> _excessWages = 
    new Overridable<decimal?>(CalculateExcessWages, (x,y) => x == y); 
public virtual decimal? ExcessWages 
{ 
    get 
    { 
     return _excessWages.Value; 
    } 
    set 
    { 
     _excessWages.Value = value; 
    } 
} 

我遇到了這個唯一的問題是,CalculateExcessWages是一個非靜態方法,因此不能在現場初始使用。由於我的類中的所有屬性都是非靜態的,因此我必須初始化構造函數中的所有支持字段。

0

這對我來說看起來很合理。我可能會唯一的變化是緩存CalculateExcessWages(),如果它是昂貴的事,並確定以高速緩存:

private Nullable<decimal> _excessWages; 
private Nullable<decimal> _excessWagesCalculated; 
public virtual decimal ExcessWages 
{ 
    get 
    { 
     if (_excessWagesCalculated == null) 
      _excessWagesCalculated = CalculateExcessWages(); 
     return _excessWages ?? _excessWagesCalculated; 
    } 
    set 
    { 
     if (_excessWagesCalculated == null) 
      _excessWagesCalculated = CalculateExcessWages(); 
     if (value != _excessWagesCalculated) 
      _excessWages = value; 
     else 
      _excessWages = null; 
    } 
} 

但是,這比你的代碼,我認爲你正在尋找簡化。

+0

嗯,有趣。這可能也可能不是問題。許多這些可覆蓋的屬性是從其他可覆蓋的屬性計算出來的,這些屬性是從其他屬性計算出來的,等等。因此,每次屬性訪問或更改時都會啓動一系列計算。從數據庫加載對象時,會發生這種情況,用戶將覆蓋其中一個屬性,或者用戶打印報告或序列化對象。需要思考的東西。 – 2010-02-26 16:46:43

1

你可以爲此做一個類包裝器。

class OverridableValue<T> 
{ 
    public OverridableValue<T>(Func<T> calculator) 
    { 
     _calculator = calculator; 
    } 
    private Nullable<T> _t; 
    private Func<T> _calculator; 
    public T Get() 
    { 
     return return _t ?? _calculator(); 
    } 
    public void Set(T value) 
    { 
     _t = (value != _calculator()) ? value : null; 
    } 
} 

它的語法不太好,但至少可以節省一些擊鍵。

現在你可以使用這樣的:

class Foo 
{ 
    OverridableValue<decimal> _excessWages = 
      new OverridableValue<decimal>(CalculateExcessWages); 
    public decimal ExcessWages 
    { 
     get { return _excessWages.Get(); } 
     set { _excessWages.Set(value); } 
    } 
    ... 
} 

的優點是整個邏輯是在類隱。

+0

並非所有可覆蓋的屬性都是值類型,所以我需要修改它以便'_t'可以被賦予任何類型,而不僅僅是可以爲空。 – 2010-02-26 16:56:30

+0

你基本上需要2個泛型:一個用'where T:class',另一個用於值類型。 – Vlad 2010-02-26 16:59:30

1

你可以通過定義一組實用的操作/讓helper方法

private static T GetUtil<T>(ref Nullable<T> value, Func<T> calc) { 
    return value ?? calc(); 
} 

private static void SetUtil<T>(ref Nullable<T> value, T newValue, Func<T> calc) { 
    if (newValue != calc()) { 
    value = newValue 
    } else { 
    value = null; 
    } 
} 

private Nullable<decimal> _excessWages; 
public decimal ExcessWages 
{ 
    get { return GetUtil(ref _excessWages, CalculateExcessWages); } 
    set { SetUtil(ref _excessWages, value CalculateExcessWages); } 
}