I have a lot of classes that have numeric values that are configurable by the customer during runtime and should be clamped between a min and a maximum value. The value changes should also be logged. An example of this is given below:
public class MyClass
{
public double AMin { get; set; }
public double AMax { get; set; }
public double A { get; private set; }
public double BMin { get; set; }
public double BMax { get; set; }
public double B { get; private set; }
public double CMin { get; set; }
public double CMax { get; set; }
public double C { get; private set; }
public bool SetA(double value)
{
double clamped = Clamp(value, AMin, AMax);
if (clamped != value)
{
LogError("New value: {value} for A is out of range.");
return false;
}
else
{
LogError("Set A to: {value}.");
return true;
}
}
public bool SetB(double value)
{
double clamped = Clamp(value, BMin, BMax);
if (clamped != value)
{
LogError("New value: {value} for B is out of range.");
return false;
}
else
{
LogError("Set B to: {value}.");
return true;
}
}
public bool SetC(double value)
{
double clamped = Clamp(value, CMin, CMax);
if (clamped != value)
{
LogError("New value: {value} for C is out of range.");
return false;
}
else
{
LogError("Set C to: {value}.");
return true;
}
}
public static double Clamp(double value, double min, double max)
{
if (value <= min)
{
_value = min;
return;
}
if (value >= max)
{
_value = max;
return;
}
_value = value;
}
}
As the number of values grow, you can see this has me writing boilerplate code and it's easy to make copy/paste errors. I could make a Visual Studio snippet for this, but I was wondering if it would be a good solution if I would refactor this repetition into a class, so that it would look like below:
public class MyClass
{
public Parameter A { get; init; } = new Parameter("A");
public Parameter B { get; init; } = new Parameter("B");
public Parameter C { get; init; } = new Parameter("C");
...
}
public class Parameter
{
public string Name { get; set; } = "";
public double Min { get; set; } = double.MinValue;
public double Max { get; set; } = double.MaxValue;
public double Value { get => _value; }
private double _value = 0.0;
public Parameter(string name)
{
Name = name;
}
public double GetValue()
{
return _value;
}
// returns true on success
public bool SetValue(double value)
{
if (value <= Min)
{
_value = Min;
Log.Error($"New value: {value} for {Name} too low.");
return false;
}
if (value >= Max)
{
_value = Max;
Log.Error($"New value: {value} for {Name} too high.");
return false;
}
Log.Info($"Set {Name} to: {value}.");
_value = value;
}
}
Although this would have me writing MyInstance.MyParameter.Value
instead of MyInstance.MyParameter
all the time, and it "violates" the law of Demeter, I think in the end it will save me a lot of time and I think it would be more maintainable.
Is it a common practice to do something like this, or is there a big downside that I am not yet seeing?
Side-note: I work in industrial automation, more specific with PLCs, so validation using exceptions is not an option for me. I have a C# background, hence the example is given in that language.
GetValue
method is redundant to the getter of your pre-existingValue
property