Elegant way to validate values

10,829

Solution 1

Assuming you want this sort of behaviour, you might consider some helper methods, e.g.

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 this for various types, you could even make it generic:

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...

Solution 2

All depends what technology you are using - if you're under MVC you can use Attributes, like this;

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

Solution 3

Here's my version, it's a bit cleaner than Jon's version in some respects:

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;
}

Solution 4

Try to use such a method:

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

        proceedFunction();
    }

Solution 5

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);

Also you can implement your own validation attributes overriding ValidationAttribute class

Share:
10,829
kyrylomyr
Author by

kyrylomyr

Updated on July 03, 2022

Comments

  • kyrylomyr
    kyrylomyr almost 2 years

    I have a class with many fields which represents different physical values.

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

    Each field is exposed using read/write property. I need to check on setter that the value is correct and generate exception otherwise. All validations 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 any elegant and flexible way to accomplish such validation?

  • kyrylomyr
    kyrylomyr almost 13 years
    Thanks for improving code. But I hasn't understood what you mean about "i18n-friendly"...
  • Jon Skeet
    Jon Skeet almost 13 years
    @archer: The messages are hard-coded as English. You'll have a bit more work ahead of you if you want to translate them into a different language.
  • kyrylomyr
    kyrylomyr almost 13 years
    OK. Thank you for explanation.
  • jp2code
    jp2code almost 13 years
    RATS! I like this one much better than the little validator routine I just wrote. I even invoked Thy name! :)
  • Skizz
    Skizz almost 13 years
    This seems like it's going to introduce a bit of overhead surely?
  • Kirill Polishchuk
    Kirill Polishchuk almost 13 years
    @Skizz, It is validation using attributes, no more. I don't like implement validation inside set accessor.
  • Skizz
    Skizz almost 13 years
    what I was getting at was the run time connection between the property and the attribute. Maybe .net does something clever here.
  • Kirill Polishchuk
    Kirill Polishchuk almost 13 years
    @Skizz, It depends on task, but many .NET frameworks such Entity Framework, WPF, ASP.NET MVC, etc use reflection. This validation is something like simple AOP version.
  • Mikey Hogarth
    Mikey Hogarth almost 13 years
    Neither was I - the MVC attributes handle both client side and server side validation (see the Model.IsValid method)