Je suis un programmeur "old school" ici qui a du mal à utiliser l'héritage à mon avantage. Je me suis retrouvé à répéter le code et ça a commencé à sentir. Je n'étais pas adhérant à DRY, donc j'essaie de refactoriser un peu ici pour réduire la duplication de code! J'essaie d'écrire des classes d'objets de valeur à utiliser dans mes entités, qui imposeront des invariants de base. J'ai une classe abstraite ValueObject générique qui gère l'égalité et hash comme ceci:Concrete Class hériter de la classe Abstract qui hérite de la classe Generic Abstract
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;
}
}
J'ai été alors crée mes classes d'objets de valeur qui mettent alors en œuvre cette classe abstraite, et fournir la logique pour vous assurer que l'objet ne peut pas être créé dans un état invalide. C'est à ce moment-là que j'ai commencé à violer DRY, et que je créais de nombreux objets avec le même code (par exemple, une chaîne obligatoire avec une longueur maximale de 50, 30 ou 10). Je souhaite donc mettre le code qui impose l'invariant dans sa propre classe, et faire en sorte que ma classe d'objets de valeur concrète hérite de cette capacité. Quelque chose comme (cela ne compile pas, voir ci-dessous):
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;
}
}
}
alors je pourrais « utiliser » toutes ces fonctionnalités dans la classe concrète comme ceci:
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 };
}
}
Tout cela semble comme un moyen raisonnable de résoudre le problème (pour moi). Le problème est que je reçois une erreur de compilation dans la déclaration RequiredStringValueObject:
Le type
string
ne peut pas être utilisé comme un paramètre de typeT
dans le type générique ou méthodeValueObject<T>
. Il n'y a pas de conversion de référence implicite destring
àValueObject<string>
.
Je ne comprends pas exactement le message d'erreur. Est ce que j'essaye de faire possible? Y a-t-il un moyen de faire ce travail? Ou y a-t-il une autre approche que je pourrais/devrait prendre?
Je ne suis pas sûr, mais pourquoi avez-vous le 'où T: ValueObject' –
TryingToImprove
Erreur est due à la façon dont la classe est définie. Vous avez 'la classe abstraite publique ValueObject où T: ValueObject '. Essentiellement, vous dites - je déclare une classe générique avec T étant limité aux instances de cette même classe. Sort de définition circulaire. 'String' ne satisfait pas à ce critère. –
Sherlock