Elegant way to check values

I have a class with many fields that represent different physical values.

class Tunnel { private double _length; private double _crossSectionArea; private double _airDensity; //... 

Each field is displayed using the read / write property. I need to check setter that the value is correct and throw an exception otherwise. All checks are similar:

  public double Length { get { return _length; } set { if (value <= 0) throw new ArgumentOutOfRangeException("value", "Length must be positive value."); _length = value; } } public double CrossSectionArea { get { return _crossSectionArea; } set { if (value <= 0) throw new ArgumentOutOfRangeException("value", "Cross-section area must be positive value."); _crossSectionArea = value; } } public double AirDensity { get { return _airDensity; } set { if (value < 0) throw new ArgumentOutOfRangeException("value", "Air density can't be negative value."); _airDensity = value; } } //... 

Is there an elegant and flexible way to do this check?

+8
c # validation
source share
7 answers

Assuming you want this behavior, you can consider some helper methods, for example

 public static double ValidatePositive(double input, string name) { if (input <= 0) { throw new ArgumentOutOfRangeException(name + " must be positive"); } return input; } public static double ValidateNonNegative(double input, string name) { if (input < 0) { throw new ArgumentOutOfRangeException(name + " must not be negative"); } return input; } 

Then you can write:

 public double AirDensity { get { return _airDensity; } set { _airDensity = ValidationHelpers.ValidateNonNegative(value, "Air density"); } } 

If you need it for different types, you can even make it general:

 public static T ValidateNonNegative(T input, string name) where T : IComparable<T> { if (input.CompareTo(default(T)) < 0) { throw new ArgumentOutOfRangeException(name + " must not be negative"); } return input; } 

Note that none of this is terribly i18n-friendly ...

+6
source share

It all depends on the technology you use - if you are under MVC, you can use attributes, for example:

http://msdn.microsoft.com/en-us/library/ee256141(v=vs.98).aspx

+3
source share

Here's my version, it's a little cleaner than the Jon version in some ways:

 interface IValidator <T> { bool Validate (T value); } class IntValidator : IValidator <int> { public bool Validate (int value) { return value > 10 && value < 15; } } class Int2Validator : IValidator<int> { public bool Validate (int value) { return value > 100 && value < 150; } } struct Property<T, P> where P : IValidator<T>, new () { public T Value { set { if (m_validator.Validate (value)) { m_value = value; } else { Console.WriteLine ("Error validating: '" + value + "' is out of range."); } } get { return m_value; } } T m_value; static IValidator<T> m_validator=new P(); } class Program { static void Main (string [] args) { Program p = new Program (); p.m_p1.Value = 9; p.m_p1.Value = 12; p.m_p1.Value = 25; p.m_p2.Value = 90; p.m_p2.Value = 120; p.m_p2.Value = 250; } Property<int, IntValidator> m_p1; Property<int, Int2Validator> m_p2; } 
+3
source share

Try using this method:

  public void FailOrProceed(Func<bool> validationFunction, Action proceedFunction, string errorMessage) { // !!! check for nulls, etc if (!validationFunction()) { throw new ArgumentOutOfRangeException(errorMessage); } proceedFunction(); } 
+1
source share

You can achieve this using classes from System.ComponentModel.DataAnnotations

 class Tunnel { [Range(0, double.MaxValue, ErrorMessage = "Length must be positive value.")] public double Length { get; set; } } 

Validation:

 var tunnel = new Tunnel { Length = 0 }; var context = new ValidationContext(tunnel, null, null); Validator.ValidateObject(tunnel, context, true); 

You can also implement your own validation attributes by overriding the ValidationAttribute class

+1
source share

Yes, by creating your own validation attributes.

Read this article: Validating business objects using attributes in C #

I will have decency NOT to copy it here :)

+1
source share

Using the Validator function mentioned in my comment above, I would do something like this (unverified code):

 void textBox_Changed(object sender, EventArgs e) { submitButton.Enabled = validator(); } bool validator() { const string NON_POSITIVE = "Value must be greater than Zero"; bool result = false; string controlName = "Length"; try { _length = Convert.ToDouble(txtLength.Text); if (_length <= 0) throw new Exception(NON_POSITIVE); controlName = "Cross Section Area"; _crossSectionArea = Convert.ToDouble(txtCrossSectionArea.Text); if (_crossSectionArea <= 0) throw new Exception(NON_POSITIVE); controlName = "Air Density"; _airDensity = Convert.ToDouble(txtAirDensity.Text); if (_airDensity <= 0) throw new Exception(NON_POSITIVE); result = true; // only do this step last } catch (Exception err) { MessageBox.Show(controlName + " Error: " + err.Message, "Input Error"); } return result; } 

John Skeet probably has a better way, but it works. :)

+1
source share

All Articles