Use "constraints" instead of argument validation for int and double etc

I will still continue with the argument validation track in this post also, I think it's an interesting topic.

If you haven't read my earlier posts about this topic, take a look at the following posts on my blog:

http://weblogs.asp.net/fredriknormen/archive/2008/05/07/lazy-developers.aspx

http://weblogs.asp.net/fredriknormen/archive/2008/05/08/how-to-validate-a-method-s-arguments.aspx

This time I will focus on argument of type int and double etc. Lets assume we have a method which should be used to set a price on a product:

public class Product
{
    public void SetPrice(double price)
    {
        price.RequireArgument("price")
             .GreaterOrEqualTo(0);

        //..
    }
}

Note: I will use my friend Roger Alsing cool argument validation solution in this example you can read more about it on his blog.

I use a Set method instead of a property in this example.

I use a validation here to make sure price is not lesser then 0. Here is another method that takes three argument, year, month and day as integers:

public void SetDate(int year, int month, int day)
{
    year.RequireArgument("year")
        .InRange(1900, 3000);

    month.RequireArgument("month")
        .InRange(1, 12);

    day.RequireArgument("day")
       .InRange(1, 31);

   //...
}

Note: The Day argument here is special, as we know there is not always 31 days in each month, so another kind of validation is needed, but I make this code only simple for this post.

Now lets change this code and make sure the int and double is not an argument that can take a number larger and lesser than the validation. What we do is to create a Price, Year, Month and a Day class and use them as an argument instead of double and int.

public class Price
{
       private double _price;

       public Price(double price)
       {
           price.RequireArgument("price")
                .GreaterOrEqualTo(0);

           this._price = price;
       }

       //...
   }


   public class Year
   {
       private int _year;

       public Year(int year)
       {
           year.RequireArgument("year")
               .InRange(1900, 3000);
       }

       //...
   }


   public class Month
   {
       private int _month;

       public Month(int month)
       {
           month.RequireArgument("month")
              .InRange(1, 12);
       }
   }


   public class Product
   {
       public void SetPrice(Price price)
       {
           //..
       }
}


public void SetDate(Year year, Month month, Day day)
{
    //...
}

Note: I skip the Day struct because of not adding to much code, you still get the basic ideas from the other structs.

As you see now the method takes a Price, Year, Month and a Day object, not a double or int and it will directly add a constraint. The user of the method can't pass in a invalid value. I also moved the validation to the classes instead. The Price class could be replaced with the Money pattern Fowler mention in his book if we want it to handle Money.

What do you think, is this something you use today?

9 Comments

  • Unless 0 is a valid value, the user can still pass invalid arguments. For example:

    Year year = new Year();
    Month month;
    Day day;

    SetDate(year, month, day);
    // => SetDate(0, 0, 0)


  • Richard:
    You are right, I made the misstake and use a Struct when it should be a class, I changed it to a class with no default constructor. Thanks for the comment.


  • @Steve:
    Thanks Steve for you wonderful comment :)
    The idea was to demonstrate how an int and double etc can be turned into a “restricted” type of an argument. By using this solution we can avoid some validation in the methods. My examples was not fully implemented, it was only skeleton code, but I did actually miss the part where we could declare the variable as Year year; and that is a valid variable to pass and the validation will in that case not take place, so we would need an extra validation within the method. I was so focus on the basic idea of using an own defined type. So it was great that Richard mentioned the stuff that I didn’t even had in mind when writing the post.
    About the performance, computer today are so fast so I don’t spend so much time of using a struct or a class to increase performance, the differences are so small. It can be very important if it’s a performance critical app. I did a test where I use a Year as a class and also as a struct. Make a call to a method where I use those types as argument. I use a loop which calls the method 100 000 000 times. The result in millisecond was 1621 for using struct, and it took 1745 millisecond for using a class. But I did use a struct in the first example because of the performance issue, but when Richard mention about the Year year; and it could be passed, I changed it to class. It requires less code to use a class.
    I love this kind of comments, keep them coming!

  • Fredrik,

    Don't forget you still have to do a check for null when calling the method with a class compared to the struct ;-)

    I've done some performance measurements one my own, but in my case the method calls using a structure instead of a class were actually slower than the method calls using a class (including GC time to clean up all those created Month classes)!

    But even without these measurements or the performance loss I had to agree with you anyway with you about the performance statement. Having a few percent performance loss on some method calls in your business app sure isn’t gonna hurt ya. Having a easy and defensive API is much more important than some theoretical performance improvements. However, I think the use of structs can be very helpful in an API. When the type feels like a primitive type (and I think Year, Month and Day do) we should consider using a struct. By using a structure, we’re actually saying: “This is a primitive type”.


  • @Steve:
    Why are you people so smart? ;) Someday I will be smart also.
    Thanks for the comments!

  • You make me blush ;-)


  • @Torkel:
    This is how the Microsoft P&P team have done it with the WntLib's Validaiton block. Roget Alsing also has the same kind of solution in nPersist. The only thing that I don't like is that you need to use a proxy generator.

  • Yes I agree, the proxy generator is a big problem (you can only use it for in specific scenarios and only for interface and virtual functions, etc).

    I did not know that the Validation block and nPersist could do this kind of argument validation.

  • @Fredrik

    All examples of the Validation Block that I could find use attributes on properties. To perform the validation you have to call an external validator that checks the validation attributes.

    Using a method interceptor is a very different approach that allows function argument validation and return value validation to happen transparently. But I completely agree that because you have to use a proxy generator it is not so useful.

    I wish that method interception were built into the CLR!

Comments have been disabled for this content.