I'm stuck with what seemed to be a very simple task at the very beginning. I have a class hierarchy each class in which can define its own validation rules. Defining validation rules should be as simple as possible. Here is what is almost what is needed:
class HierarchyBase
{
private List<Func<object, bool>> rules = new List<Func<object, bool>>();
public int fieldA = 0;
public HierarchyBase()
{
AddRule(x => ((HierarchyBase)x).fieldA % 2 == 0);
}
protected virtual void Operation()
{
fieldA++;
}
protected void AddRule(Func<object, bool> validCriterion)
{
rules.Add(validCriterion);
}
public void PerformOperation()
{
Operation();
Validate();
}
protected virtual void Operation()
{
fieldA++;
}
private void Validate()
{
IsValid = rules.All(x => x(this));
}
public bool IsValid
{
get;
private set;
}
}
There is one more thing that is needed - type safety when adding validation rules. Otherwise each sub class will have to do those casts that just look awkward. Ideally Func<T, bool>
would work, but there is a whole bunch of issues with that: we cannot inherit our HierarchyBase
from any kind of IValidatable<HierarchyBase>
as the inheritance hierarchy can be N levels deep (yeah, I feel the smell as well); storing any concrete Func<HierarchyBaseInheritor, bool>
in rules
and traversing them.
How would you introduce type-safety here?
The right approach is to make each class in the hierarchy responsible for validating itself:
HierarchyBase:
class HierarchyBase
{
public int A { get; set; }
public bool Validate()
{
return this.OnValidate();
}
protected virtual bool OnValidate()
{
return (this.A % 2 == 0);
}
}
HierarchyBaseInheritorA:
class HierarchyBaseInheritorA : HierarchyBase
{
public int B { get; set; }
protected override bool OnValidate()
{
return base.OnValidate() &&
(this.A > 10) &&
(this.B != 0);
}
}
HierarchyBaseInheritorB:
class HierarchyBaseInheritorB : HierarchyBaseInheritorA
{
public int C { get; set; }
protected override bool OnValidate()
{
return base.OnValidate() &&
(this.A < 20) &&
(this.B > 0) &&
(this.C == 0);
}
}
Usage:
var result = new HierarchyBaseInheritorB();
result.A = 12;
result.B = 42;
result.C = 0;
bool valid = result.Validate(); // == true
Note: The following solution is an in-joke between me and Eric Lippert. It works, but is probably not to be recommended.
The idea is to define a generic type parameter that refers to the "current" type (like this
refers to the "current" object).
HierarchyBase:
class HierarchyBase<T>
where T : HierarchyBase<T>
{
protected readonly List<Func<T, bool>> validators;
public HierarchyBase()
{
validators = new List<Func<T, bool>>();
validators.Add(x => x.A % 2 == 0);
}
public int A { get; set; }
public bool Validate()
{
return validators.All(validator => validator((T)this));
}
}
HierarchyBaseInheritorA:
class HierarchyBaseInheritorA<T> : HierarchyBase<T>
where T : HierarchyBaseInheritorA<T>
{
public HierarchyBaseInheritorA()
{
validators.Add(x => x.A > 10);
validators.Add(x => x.B != 0);
}
public int B { get; set; }
}
HierarchyBaseInheritorB:
class HierarchyBaseInheritorB : HierarchyBaseInheritorA<HierarchyBaseInheritorB>
{
public HierarchyBaseInheritorB()
{
validators.Add(x => x.A < 20);
validators.Add(x => x.B > 0);
validators.Add(x => x.C == 0);
}
public int C { get; set; }
}
Usage:
var result = new HierarchyBaseInheritorB();
result.A = 12;
result.B = 42;
result.C = 0;
bool valid = result.Validate(); // == true
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.