Just add a constructor:
private double _MinValue, _MaxValue; // no readonly keyword
public ValidateCustomAttribute(double min, double max, Func<string> errorMessageAccessor)
: base(errorMessageAccessor)
{
_MinValue = min;
_MaxValue = max;
}
What you can't do is have variables in the attribute constructor invokation.
This is not possible:
[ValidateCustom(min, max)]
But if you use literals (or constants) in your code you can have these:
[ValidateCustom(1, 1000)]
And on another class or method:
[ValidateCustom(3, 45)]
What you are missing is the constructor taking in those static values and affixing them to the construct you are describing with your attribute.
EDIT: The ugly way around this
If you really really need this, you can circumvent the limitation but it is as ugly as it can get. I am STRONGLY against this, but you asked for it...
- categorize your data
- map categories to binding symbols
- use binding symbols
- resolve binding symbols to data
So, let's get to work:
1) categorize your data
Say, your data is a range (min, max)
, the first thing to do is establish which values are possible, let's say you have 4 possible ranges (may be hundreds, but that's anther problem altogether).
(1, 1000)
(10, 20)
(3, 45)
(5, 7)
2) map categories to binding symbols
Now you have to use an enum
as binding symbols for those ranges:
public enum MyRanges
{
R1, R2, R3, R4
}
3) use binding symbols
Define the constructor as taking in the binding symbol:
private MyRanges _R;
public ValidateCustomAttribute(MyRanges r, Func<string> errorMessageAccessor)
: base(errorMessageAccessor)
{
_R = r;
}
The attribute will be used like this:
[ValidateCustom(MyRanges.R2, "ERROR!")]
4) resolve binding symbols to data
The last you need is a dictionary with the actual data:
Dictionary<MyRanges, double> dataMin = {
{ MyRanges.R1, 1},
{ MyRanges.R2, 10},
{ MyRanges.R3, 3},
{ MyRanges.R4, 5}
};
Dictionary<MyRanges, double> dataMax = {
{ MyRanges.R1, 1000},
{ MyRanges.R2, 20},
{ MyRanges.R3, 45},
{ MyRanges.R4, 7}
};
The test will use the binding this way:
public override bool IsValid(object value)
{
double val = (double)value;
return val >= dataMin[_R] && val <= dataMax[_R]; // get data through binding
}
Now you can change behind the scenes those values and all attributes bound by the binding symbols behave differently:
dataMax[MyRanges.R4] = 29;
Done.
What cannot change is now the binding from attribute to category, but the data contained in the category is free to change.
But ugly and impossible to maintain. Don't do it, really.