Implicit
24 Jun 2008A few days back, I came across an article by Bill Wagner, on the topic of validating the state of your objects. His approach, in essence, is to override the bool operator on your class, and use it to return a boolean indicating whether the instance is in a valid state. Simplified to the extreme, the code would look something like this:
public class Person
{
public string Name
{ get; set; }
public static implicit operator bool( Person person )
{
return ( person.Name.Length > 0 );
}
}
Regardless of what I think of the approach, I was initially puzzled by the line:
public static implicit operator bool( Person person )
I had never encountered the keyword “implicit” before in C#, and was therefore not too sure of what was happening there.
So I checked the C# documentation, where I learnt that “The **implicit **keyword is used to declare an implicit user-defined type conversion operator”. In other words, it defines an “automatic” cast to the specified type, without requiring the usual explicit and cumbersome syntax.
In our example, an instance of Person can be directly converted to a bool, and will be evaluated as “true” only if its name is not empty - and the corresponding NUnit test below passes:
[Test]
public void CheckState()
{
Person person = new Person();
person.Name = "Albert";
Assert.IsTrue( person );
person.Name = "";
Assert.IsFalse( person );
}
This got me even more curious, and I wondered what would happen if a class were to provide multiple implicit conversions, to various types, along these lines:
public class Person
{
public string Name
{ get; set; }
public static implicit operator bool( Person person )
{
return ( person.Name.Length > 0 );
}
public static implicit operator int( Person person )
{
return person.Name.Length;
}
}
This one builds like a charm, and the following test passes:
[Test]
public void CheckImplicit()
{
Person albert = new Person();
albert.Name = "Albert";
Person betty = new Person();
betty.Name = "Betty";
Assert.AreEqual( true, albert && betty );
Assert.AreEqual( 11, albert + betty );
}
Pretty amazing; at that point, I got a queasy feeling, and started wondering if I was back in the land of the dreaded VBA “variant” type, where everything converts to anything, with painfully unexpected results. So I tried the following:
public static implicit operator bool( Person person )
{
return (person.Name.Length > 0);
}
public static implicit operator int( Person person )
{
return person.Name.Length;
}
public static implicit operator string( Person person )
{
return person.Name;
}
I was very interested to see what would happen there:
[Test]
public void CheckImplicit()
{
...
Assert.AreEqual( 11, albert + betty );
}
But I never got that far - as soon as I hit “build”, I received the following error message:
Operator ‘+’ is ambiguous on operands of type ‘ObjectState.Person’ and ‘ObjectState.Person
I will let the interested reader look into how user-defined implicit conversions are processed; it’s not exactly an easy read, and I confess I gave up rapidly (that would make for a nasty interview question).
For more goodness on implicitness, I enjoyed this other post, which focuses more on polymorphism and generics questions.
Ah, and regarding the initial question, that is, using the bool conversion to check the validity of an object, I thought it was an interesting approach, and definitely enjoyed seeing a syntax I was not familiar with. On the other hand, I am slightly unconvinced by the approach altogether. Wagner suggests that “once you’ve got an operator bool in your type, you can call it from inside a specific public method”. I assume that method would be called something like “IsInValidState()”, but in that case I don’t fully see the benefit of overriding the operator in the first place. There is a clear benefit to his approach: it helps reduce expressions like:
if (instance1 && instance2 && instance3)
At the same time, what is gained in conciseness, in my opinion, is lost in readability; I don’t expect a class to evaluate as a bool, and nothing indicates that the bool has to do with the validity of its internal state. I would still favor having explicit guard clauses, validating specific invariants of my object. But I guess at that point, this is more a matter of taste.