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?

Published Saturday, May 10, 2008 1:21 PM by Fredrik N

Comments

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

Saturday, May 10, 2008 9:53 AM by Richard

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)

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

Saturday, May 10, 2008 11:03 AM by Fredrik N

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.

# Interesting Finds: May 10, 2008

Saturday, May 10, 2008 12:15 PM by Jason Haley

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

Sunday, May 11, 2008 6:43 AM by Steven

Fredrik,

Consider using structs again. Using classes for those simple types can have a huge impact on performance. The penalty is about the same as with boxing.

But when you use structs, you'll have to protect yourself from invalid default values. The Framework Design Guidelines give great guidance here: "Do ensure that a state where all instance data is set to zero, false, or null (as appropriate) is valid".

Looking at the Year struct, you've got two options:

1. The year 0 is a valid year. In that case your method must (again) have a validation for year >= 1900.

2. The year 1900 and up are valid. In that case the Year(1900) will be the same as Year(), so 1900 must be translated to 0. Below I'll show the code for the Month struct.

But there is another thing we should be careful about. When you define a Year struct that is only valid for the years 1900 till 3000, you'll limit the use of this type. What if you have a function that can take a year from 1800 till 2900. Do we use the same Year type? If not, what name do we give that second Year type? Year2? Year1800till2900? Yuck! Besides that, you gave the type an extremely general purpose name: Year. Then you’d better be sure that class is usable for many people in many circumstances. I think you can't be to careful about naming your (public) types.

But this is perhaps a different discussion, so let's get back to the original class/struct discussion. Below is a implementation of a Month struct. The trick here is that an default instance “new Month()” has the value for “January”.

[DebuggerDisplay("Month ({ToString()})")]

public struct Month

{

private readonly byte _month;

public Month(int month)

{

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

this._month = (byte)(month - 1);

}

public int Value { get { return this._month + 1; } }

public override string ToString()

{

return CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(this.Value);

}

public override int GetHashCode()

{

return this._month.GetHashCode();

}

// Operator can be implicit, because it will never throw an exception

public static implicit operator int(Month month) { return month.Value; }

// Operator must be explicit, because it can throw throw an exception

public static explicit operator Month(int month) { return new Month(month); }

}

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

Sunday, May 11, 2008 10:28 AM by Fredrik N

@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!

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

Sunday, May 11, 2008 10:47 AM by Steven

Oww... I forgot to implement the IEquatable and IComparable interfaces on the Month :). The type should be extended as follows:

[TypeConverter(typeof(MonthConverter))]

[DebuggerDisplay("Month ({ToString()})")]

public struct Month : IEquatable<Month>, IComparable<Month>, IComparable

{

[...]

public bool Equals(Month other) { return this._month.Equals(other._month); }

public override bool Equals(object obj)

{

if (obj == null) return false;

if (!(obj is Month)) return false;

return Equals((Month)obj);

}

public int CompareTo(Month other) { return this._month.CompareTo(other._month); }

public int CompareTo(object obj)

{

obj.RequireArgument("obj").NotNull();

obj.RequireArgument("obj").OfType<Month>();

return CompareTo((Month)obj);

}

}

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

Sunday, May 11, 2008 3:10 PM by Steven

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

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

Monday, May 12, 2008 5:03 AM by Fredrik N

@Steve:

Why are you people so smart? ;) Someday I will be smart also.

Thanks for the comments!

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

Monday, May 12, 2008 4:05 PM by Steven

You make me blush ;-)

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

Tuesday, May 13, 2008 8:05 AM by Torkel

I was able to get this to work using a  proxy generator and a method interceptor:

public interface IRegistrationService

{

 void Register([NotNull] string name, [InRange(5, 10)] int value);

 void Register([NotNullOrEmpty] string name);

}

www.codinginstinct.com/.../argument-validation-using-attributes.html

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

Tuesday, May 13, 2008 8:14 AM by Fredrik N

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

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

Tuesday, May 13, 2008 8:22 AM by Torkel

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.

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

Tuesday, May 13, 2008 10:40 AM by Torkel

@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!

Leave a Comment

(required) 
(required) 
(optional)
(required)