我是一名“老派”程序员,正在努力将继承用于我的优势。我发现自己重复了代码,它开始闻起来。我没有坚持干,所以我试图在这里重构一下,以减少代码重复!
我正在尝试编写要在我的实体中使用的值对象类,这将强制执行基本不变量。我有一个通用的抽象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
不能用作泛型类型或方法T
中的类型参数ValueObject<T>
。没有从string
到ValueObject<string>
的隐式引用转换。
我完全不理解错误消息。我正在尝试做什么?有没有办法让这项工作?或者我可以/应该采取另一种方法吗?
答案 0 :(得分:4)
您的通用类型T上有一个where子句:
public abstract class ValueObject<T> where T : ValueObject<T>
这告诉编译器T必须从ValueObject派生,而string不是。
你想用哪个T:子句强制实施?你可能想省略它。
答案 1 :(得分:2)
问题源于这一行:
abstract class ValueObject<T> where T : ValueObject<T>
您需要T
从ValueObject<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)
{
}
然后你可以传递一组方法作为一个单独的对象。
答案 2 :(得分: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;
}
}
答案 3 :(得分: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 };
}
}
具体实现, FirstName (一个必需的基于字符串的值对象,具有最大长度):
public class FirstName : RequiredStringValueObject
{
private FirstName(string value) : base(nameof(FirstName),30, value) { }
public static FirstName Create(string value)
{
return new FirstName(value);
}
}
作为80后的孩子会说,&#34; Totally tube!&#34;
谢谢!