2017-02-07 195 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的孩子会说,“完全管!”

谢谢!