2017-02-07 192 views
1

我是一個「老派」程序員,在這裏正在努力繼承我的優勢。我發現自己重複了代碼,並開始聞起來。我沒有堅持DRY,所以我試圖在這裏重構一下,以減少代碼重複!具體類繼承自繼承自通用抽象類的抽象類

我試圖寫在我的實體中使用Value對象類,這將強制執行基本的不變量。我有一個處理平等和哈希像這樣一個通用的抽象的ValueObject類:

public abstract class ValueObject<T> where T : ValueObject<T> 
{ 
    protected abstract IEnumerable<object> GetEqualityCheckAttributes(); 

    public override bool Equals(object other) 
    { 
     return Equals(other as T); 
    } 

    public bool Equals(T other) 
    { 
     if (other == null) 
     { 
      return false; 
     } 
     return GetEqualityCheckAttributes().SequenceEqual(other.GetEqualityCheckAttributes()); 
    } 

    public static bool operator == (ValueObject<T> left, ValueObject<T> right) 
    { 
     return Equals(left, right); 
    } 

    public static bool operator != (ValueObject<T> left, ValueObject<T> right) 
    { 
     return !(left == right); 
    } 

    public override int GetHashCode() 
    { 
     int hash = 17; 
     foreach (var obj in this.GetEqualityCheckAttributes()) 
     { 
      hash = hash * 31 + (obj == null ? 0 : obj.GetHashCode()); 
     } 
     return hash; 
    } 
} 

我一直然後創建我的價值的對象類,然後實現這個抽象類,並提供以確保對象的邏輯不能被創建爲無效狀態。這是當我開始違反DRY時,並且使用相同的代碼創建了許多對象(例如,最大長度爲50或30或10的所需字符串)。

所以我希望把強制不變的代碼放在它自己的類中,讓我的具體值對象類繼承那個能力。喜歡的東西(這並不編譯,見下文):

public abstract class RequiredStringValueObject : ValueObject<string> 
{ 
    private string _value; 
    protected string _fieldName; 
    protected byte _maxLength; 

    public string Value 
    { 
     get 
     { 
      return _value; 
     } 
     protected set 
     { 
      if (value == null || string.IsNullOrWhiteSpace(value)) 
      { 
       throw new ArgumentNullException(_fieldName, _fieldName + " must be supplied."); 
      } 
      value = value.Trim(); 
      if (value.Length > _maxLength) 
      { 
       throw new ArgumentOutOfRangeException(_fieldName, value, _fieldName + " can't be longer than " + _maxLength.ToString() + " characters."); 
      } 
      _value = value; 
     } 
    } 
} 

然後,我可以「使用」所有這些功能的具體類,像這樣:

public class FirstName : RequiredStringValueObject 
{ 
    private FirstName(string value, string FieldName, byte MaxLength) 
    { 
     _fieldName = FieldName; 
     _maxLength = MaxLength; 
     Value = value; 
    } 
    public static FirstName Create(string value, string FieldName, byte MaxLength) 
    { 
     return new FirstName(value, FieldName, MaxLength); 
    } 

    protected override IEnumerable<object> GetEqualityCheckAttributes() 
    { 
     return new List<object> { Value }; 
    } 
} 

所有這一切都似乎是一個解決問題的合理方法(對我)。問題是我得到的RequiredStringValueObject聲明編譯器錯誤:

類型string不能用作泛型類型或方法ValueObject<T>類型參數T。沒有從stringValueObject<string>的隱式參考轉換。

我完全不明白錯誤消息。我正在嘗試做什麼?有沒有辦法做到這一點?還是有另一種方法我可以/應該採取?

+0

我不確定,但爲什麼你有'where T:ValueObject ' – TryingToImprove

+1

錯誤是由於如何定義類。你有'公共抽象類ValueObject 其中T:ValueObject '。基本上你在說 - 我在聲明一個T類的泛型類,這個類只限於這個類的實例。循環定義。 '字符串'不符合那個標準。 – Sherlock

回答

2

的問題,從這一行莖:

abstract class ValueObject<T> where T : ValueObject<T> 

您需要TValueObject<T>所以繼承,當你寫:

RequiredStringValueObject : ValueObject<string> 

string不從ValueObject(顯然)繼承,所以你需要繼承ValueObject<ValueObject<string>>,除了違反了約束條件,以及...它的海龜一直向下。

簡單的解決方法是刪除類型約束;它看起來像你的代碼大多設置爲處理object任何方式,所以你不應該需要它。把任何種類的「遞歸」類型約束放在這個設置中會導致你的問題。如果你真的需要這樣的事情,你可能需要使用成分去替代,是這樣的:

public interface IValueMethods<T> 
{ 
    //required methods 
} 

//Constructor for value object 
public ValueObject<T>(IValueMethods<T> commonMethods) 
{ 
} 

然後你就可以在一套方法作爲一個單獨的對象使用過。

+0

刪除約束然後將問題緩存到我的ValueObject抽象類中。在Equals方法中,我然後得到「類型參數'T'不能與'as'運算符一起使用,因爲它沒有類類型約束和'類'約束。」所以我從'其他作爲T'改爲'其他作爲對象'? – Scuzzlebutt

+0

另外,other.GetEqualityCheckAttributes()則有問題:「'T'沒有包含'GetEqualityCheckAttributes'的定義,也沒有找到接受'T'類型的第一個參數的擴展方法'GetEqualityCheckAtributes'」 – Scuzzlebutt

+1

@Scuzzlebutt你會可能不得不用'上面的接口建議來修改'GetEqualityCheckAttributes'。至於第一個,你可以放一個'where T:class'約束,但我真的懷疑你*想*,因爲它似乎你希望它與int,short等一起工作。 – BradleyDotNET

4

你有where子句您泛型類型T:

public abstract class ValueObject<T> where T : ValueObject<T> 

這都說明,T必須從獲得的ValueObject編譯器和字符串沒有。

你試圖強制執行與T:子句?您可能想要省略它。

2

與@BradleyDotNET所說的一致。一個可能的修復可能看起來像以下:

public abstract class ValueObjectBase 
{ 
    public abstract IEnumerable<object> GetEqualityCheckAttributes(); 
} 

public abstract class ValueObject<T> : ValueObjectBase where T : class 
{ 
    public override bool Equals(object other) 
    { 
     if (other is ValueObjectBase) 
      return Equals(other as ValueObjectBase); 

     return Equals(other as T); 
    } 

    public bool Equals(T other) 
    { 

     if (other == null) 
     { 
      return false; 
     } 
     return other.Equals(this); 

    } 

    public bool Equals(ValueObjectBase other) 
    { 
     return GetEqualityCheckAttributes().SequenceEqual(other.GetEqualityCheckAttributes()); 
    } 

    public static bool operator ==(ValueObject<T> left, ValueObject<T> right) 
    { 
     return Equals(left, right); 
    } 

    public static bool operator !=(ValueObject<T> left, ValueObject<T> right) 
    { 
     return !(left == right); 
    } 

    public override int GetHashCode() 
    { 
     int hash = 17; 
     foreach (var obj in this.GetEqualityCheckAttributes()) 
     { 
      hash = hash * 31 + (obj == null ? 0 : obj.GetHashCode()); 
     } 
     return hash; 
    } 
} 
+0

感謝您的想法!我從來沒有想過這件事。 – Scuzzlebutt

+0

它幫助完成@BradleyDotNET的啓動...... – Scuzzlebutt

0

感謝所有的幫助,這裏是最後的工作方案:

的ValueObject

public abstract class ValueObjectBase 
{ 
    public abstract IEnumerable<object> GetEqualityCheckAttributes(); 
} 

public abstract class ValueObject<T> : ValueObjectBase 
{ 
    public override bool Equals(object other) 
    { 
     if (other is ValueObjectBase) 
     { 
      return Equals(other as ValueObjectBase); 
     } 
     return Equals(other as IEquatable<T>); 
    } 

    public bool Equals(T other) 
    { 
     if (other == null) 
     { 
      return false; 
     } 
     return other.Equals(this); 
    } 

    public bool Equals(ValueObjectBase other) 
    { 
     return GetEqualityCheckAttributes().SequenceEqual(other.GetEqualityCheckAttributes()); 
    } 

    public static bool operator == (ValueObject<T> left, ValueObject<T> right) 
    { 
     return Equals(left, right); 
    } 

    public static bool operator != (ValueObject<T> left, ValueObject<T> right) 
    { 
     return !(Equals(left, right)); 
    } 

    public override int GetHashCode() 
    { 
     int hash = 17; 
     foreach (var obj in this.GetEqualityCheckAttributes()) 
     { 
      hash = hash * 31 + (obj == null ? 0 : obj.GetHashCode()); 
     } 
     return hash; 
    } 
} 

爲主題的變換中, RequiredStringValueObject

public abstract class RequiredStringValueObject : ValueObject<string> 
{ 
    private string _value; 
    protected string _fieldName; 
    protected byte _maxLength; 

    public string Value 
    { 
     get 
     { 
      return _value; 
     } 
     protected set 
     { 
      if (value == null || string.IsNullOrWhiteSpace(value)) 
      { 
       throw new ArgumentNullException(_fieldName, _fieldName + " must be supplied."); 
      } 
      value = value.Trim(); 
      if (value.Length > _maxLength) 
      { 
       throw new ArgumentOutOfRangeException(_fieldName, value, _fieldName + " can't be longer than " + _maxLength.ToString() + " characters."); 
      } 
      _value = value; 
     } 
    } 

    protected RequiredStringValueObject(string fieldName, byte maxLength, string value) 
    { 
     _fieldName = fieldName; 
     _maxLength = maxLength; 
     Value = value; 
    } 

    public override IEnumerable<object> GetEqualityCheckAttributes() 
    { 
     return new List<object> { Value }; 
    } 
} 

和具體實施中,名字(最大長度的一個必需的字符串,基於價值的對象):

public class FirstName : RequiredStringValueObject 
{ 
    private FirstName(string value) : base(nameof(FirstName),30, value) { } 

    public static FirstName Create(string value) 
    { 
     return new FirstName(value); 
    } 

} 

由於80的孩子會說,「完全管!」

謝謝!