2017-02-07 7 views
1

私は、ここで継承を使用することに苦労している "古い学校"のプログラマーです。私は自分自身がコードを繰り返していることに気がつき、それがにおいを始める。 DRYを守っていなかったので、ここでコードの重複を減らすために少しリファクタリングしようとしています!コンクリートクラスは、抽象クラスから継承する抽象クラスから継承します

私はエンティティで使用されるバリューオブジェクトクラスを作成しようとしています。エンティティは基本インバリアントを適用します。私はそうのような平等とハッシュを扱う一般的な抽象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 }; 
    } 
} 

このすべてがAのように思えます問題を解決する合理的な方法(私に)。問題は、私はRequiredStringValueObject宣言でコンパイラエラーを取得しています:

タイプstringがジェネリック型またはメソッドValueObject<T>に型パラメータTとして使用することはできません。 stringからValueObject<string>への暗黙の参照変換はありません。

エラーメッセージは正確に分かりません。私がしようとしていることは可能ですか?この仕事をする方法はありますか?または、私は/取るべきである別のアプローチがありますか?

+0

わかりませんが、なぜT:ValueObject ' – TryingToImprove

+1

エラーがクラスの定義方法に起因するのですか?あなたは 'public abstract class ValueObject ここでT:ValueObject 'を持っています。基本的には、私はTがこのクラスのインスタンスに限定されたジェネリッククラスを宣言しています。円形定義のソート。 'String'はその基準を満たしていません。 – Sherlock

答えて

2

問題は、このラインから茎:

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

あなたはあなたが書くときにValueObject<T>から継承するTを必要としている。

RequiredStringValueObject : ValueObject<string> 

stringValueObjectから継承する(明らかに)そう、あなたはありません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'演算子で使用できません。だから私は 'other as T'から 'other as object'に変わる? – Scuzzlebutt

+0

また、other.GetEqualityCheckAttributes()に問題があります: "'T'に 'GetEqualityCheckAttributes'の定義がなく、 'GetEqualityCheckAtributes'型の最初の引数 'T'を受け入れることができませんでした。" – Scuzzlebutt

+1

@Scuzzlebuttおそらく上記のインターフェースの提案で 'GetEqualityCheckAttributes'を再加工する必要があります。最初のものまでは、 'T:class'制約を置くことができますが、int、shortなどで動作させたいと思うので真剣に疑問に思っています。 – BradleyDotNET

4

あなたのジェネリック型Tにwhere句を持っている:

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; 
    } 
} 

テーマのバリエーションを、 必須の文字列値

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年代の子供が、言うように、「完全に管状!」

ありがとうございます!

関連する問題