Thursday, May 31, 2007

Quick Note

If you use regular expressions to validate email addresses, make sure you allow '+' before the '@' character. This is perfectly valid according to the RFC and some email servers let you do fun things with it. Gmail, for example, delivers anything addressed to 'youremail+anythingyouwanthere at gmail' to 'youremail at gmail'. Combined with filters that automatically label and file items, you can get email delivered directly to a specific folder. I know some unix mail systems support this stuff, too.

Don't be lazy with your regular expressions.

Tuesday, May 08, 2007

Dynamic Cast in C#

C# allows you to overload the implicit and explicit cast operators to permit your class to be converted to another, either automatically (implicit) or declaratively (explicit). All the necessary conversions are determined and applied by the compiler, at compile time. For example, if you have the following (assume A and B aren't in the same hierarchy):

A a = new B();

The compiler will look to see if B has an implicit cast operator (defined as a static method) and insert it for you if it exists, or fail with a type checking error if it doesn't. Likewise

A a = (A)(new B());

will cause the compiler to insert a call to the explicit cast operator defined in B that casts to A, or fail if it doesn't exist.

This is fine when you only need to handle conversions based on the compile-time type of your objects. If, however, you had written this extremely contrived example:

B b = new B();
object o = b;
A a = b;

This would fail to compile because the compiler is now checking the type "object" for an implicit cast to A, even though the underlying type of o is "B". If you put an explicit cast in "A a = (A)b;" it would then fail at run time, like any other invalid cast. What is really needed is a way to apply cast operators based on the run-time type of the object. This ends up being a pretty simple exercise in reflection.

public static MethodInfo GetMethod(Type toSearch, string methodName,
Type returnType, BindingFlags bindingFlags)
{
return Array.Find(
toSearch.GetMethods(bindingFlags),
delegate(Refl.MethodInfo inf)
{
return ((inf.Name == methodName) && (inf.ReturnType == returnType));
});
}

public static T DynamicCast<T>(object o)
{
Type ot = o.GetType();
MethodInfo meth = GetMethod(ot, "op_Implicit", typeof(T),
BindingFlags.Static | BindingFlags.Public);
if(meth == null)
{
meth = GetMethod(ot, "op_Explicit", typeof(T),
BindingFlags.Static | BindingFlags.Public);
}

if(meth == null) throw new InvalidCastException("Invalid Cast.");

return (T)meth.Invoke(null, new object[] { o });
}

Now we can just do

B b = new B();
object o = b;
A a = DynamicCast<A>(o);

and everything works.