inicio mail me! sindicaci;ón

Archive for January, 2010

The philosophy of technical restrictions: why value comparison is not available in .NET?

Or: Unavailability of value comparison of objects in .NET, a technical or philosophical restriction?

I was working on unit tests and then a question came to me – why comparing objects by value is not provided out-of-the-box by the .NET Framework? We all (should) know that by default, comparing objects leads to a reference comparison, unless of course you override Equals() or implement operator == on your class.

But what if I want to compare two objects from a class provided by the Framework? That would be quite handy specially for unit tests. While we have System.Object.Equals() and System.Object.ReferenceEquals() working the very same way – reference comparison – we don’t find something like System.Object.ValueEquals().

Then I started having a discussion with a friend and colleague of mine the reasons for such functionality not existing in .NET.

Although I agree that the default option should be reference comparison, I don’t see any technical limitations for not having that implemented – Reflection could be the way to do it, going recursively down each property and checking if their values are equal.

So why not? Is it something related to the theory of object orientation or was it a mere technical decision?

Let’s take one of the classical examples of OO lessons: a Car class. In a Car, you have properties such as Color, Model, Category, etc. In real life, when two cars come out of production line, while you can’t say they are the same car, one would have no problem identifying they were built by the same method. Just compare each of their properties and you will end up knowing they are not the same car, but they share the same properties and were built the same way. They don’t share a reference equality, but value equality.

As far as I am concerned, OO does not provide a specific way or method to apply the property-by-property comparison, but there isn’t a prohibition on doing that as well.

However, providing such functionality could be an expensive trade-off, since gigantic objects with collections with millions of items could be compared. It would be possible, but sometimes not practical. But then not providing that for the developer’s discretion, or at least providing ways to ensure it could be safely used? Doesn’t seems right to me.

From my point of view, such functionality could be very useful and there is no true restriction that should be applied for having something like that provided by the Framework. As I said, my opinion is that not providing it is a technical decision, and not a philosophical restriction imposed by OO theory.

What do you think? I’d be glad to listen to other opinions!

Ouvindo: The Real McKenzies – Scots Wha' Ha'e

Validation error on compiling unused Activity with custom ActivityValidator

Although I’ve worked with WWF (Windows Workflow Foundation) during all 2009, I haven’t posted anything about the technology yet, so I thought it was maybe the time to do it. I’ll start with one of the bases of the implementation of custom ActivityValidator classes. Despite being simple, that is an error that strikes new workflow developers quite often.

When creating a custom ActivityValidator, you might receive a compilation error from the newly created validator, even when the custom Activity it is targeted against is not in use yet.

But how come? Let’s take a look at the example below:

[ActivityValidator(typeof(MyCustomValidator))]
public partial class MyCustomActivity : Activity
{
    public static DependencyProperty PriceProperty = DependencyProperty.Register("Price", typeof(float), typeof(MyCustomActivity));

    [DescriptionAttribute("Price")]
    [CategoryAttribute("Price Category")]
    [BrowsableAttribute(true)]
    [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
    public float Price
    {
        get
        {
            return ((float)(base.GetValue(MyCustomActivity.PriceProperty)));
        }
        set
        {
            base.SetValue(MyCustomActivity.PriceProperty, value);
        }
    }

	public MyCustomActivity()
	{
		InitializeComponent();
	}
}

public class MyCustomValidator : ActivityValidator
{
    public override ValidationErrorCollection Validate(ValidationManager manager, object obj)
    {
        MyCustomActivity myCustomActivity = obj as MyCustomActivity;
        ValidationErrorCollection errors = base.Validate(manager, obj);

        if (myCustomActivity == null)
        {
            throw new ArgumentException("MyCustomValidator can only be used to validate MyCustomActivity instances.", "obj");
        }

        if (myCustomActivity.Price <= 0)
        {
            errors.Add(new ValidationError("'Price' must be greater than zero.", 1));
        }

        return errors;
    }
}

In the example above, when you build the project you will receive an error such as

Error 1 Activity 'MyCustomActivity' validation failed: 'Price' must be greater than zero.

Well, that is not nice. You are not even using MyCustomActivity yet!

The reason for that is simple: validators are run during runtime, but also during build time! To avoid the error, we simply check if the custom activity is in use. How? Take a peak:

public class MyCustomValidator : ActivityValidator
{
    public override ValidationErrorCollection Validate(ValidationManager manager, object obj)
    {
        MyCustomActivity myCustomActivity = obj as MyCustomActivity;
        ValidationErrorCollection errors = base.Validate(manager, obj);

        if (myCustomActivity == null)
        {
            throw new ArgumentException("MyCustomValidator can only be used to validate MyCustomActivity instances.", "obj");
        }

        if (myCustomActivity.Parent != null)
        {
            if (myCustomActivity.Price <= 0)
            {
                errors.Add(new ValidationError("'Price' must be greater than zero.", 1));
            }
        }

        return errors;
    }
}

As you can see, all we did was checking if the activity parent is not null (line 13).

Try to build now, no error will be triggered. Then drop MyCustomActivity to a workflow, don't fill in Price and build again. The error is now there, correctly returned!

Well, that was it... No black magic but surely resourceful! I hope it helps!

Ouvindo: Cólera – Vira-Latas