Я программист «старой школы», который борется с использованием наследования в свою пользу. Я обнаружил, что повторяю код, и он начал пахнуть. Я не придерживался 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
не может быть использован в качестве параметраT
типа в универсальном типе или методеValueObject<T>
. Нет никакого неявного ссылочного преобразования отstring
доValueObject<string>
.
Я не понимаю сообщение об ошибке точно. Это то, что я пытаюсь сделать возможным? Есть ли способ сделать эту работу? Или есть другой подход, который я мог бы/должен был взять?
Я не уверен, но почему у вас есть 'where T: ValueObject' –
TryingToImprove
Ошибка связана с тем, как определяется класс. У вас есть открытый публичный класс ValueObject, где T: ValueObject '. По сути, вы говорите - я объявляю общий класс, когда T ограничивается экземплярами этого самого класса. Сортировка кругового определения. 'String' не удовлетворяет этим критериям. –
Sherlock