【发布时间】:2017-02-07 19:37:48
【问题描述】:
我是这里的“老派”程序员,正在努力利用继承来发挥自己的优势。我发现自己在重复代码,它开始有异味。我没有坚持 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 };
}
}
所有这些似乎都是解决问题的合理方法(对我而言)。问题是我在RequiredStringValueObject 声明中遇到编译器错误:
类型
string不能用作泛型类型或方法ValueObject<T>中的类型参数T。没有从string到ValueObject<string>的隐式引用转换。
我不完全理解错误消息。我正在尝试做的事情可能吗?有没有办法使这项工作?或者我可以/应该采取其他方法吗?
【问题讨论】:
-
我不确定,但为什么你有
where T : ValueObject<T> -
错误是由于类是如何定义的。你有
public abstract class ValueObject<T> where T : ValueObject<T>。本质上你是在说——我声明了一个泛型类,其中 T 被限制在这个类的实例中。循环定义的排序。String不满足该标准。
标签: c# generics abstract-class c#-6.0