November 2008 - Posts

Default TypeConverters and Objects
Take a look at the following code.  Which of the following tests do you think should pass?
 

public static TypeConverter converter; [ClassInitialize()] public static void MyClassInitialize(TestContext testContext) { converter = TypeDescriptor.GetConverter(typeof(object)); } [TestMethod] public void ObjectFromString() { Assert.IsTrue(converter.CanConvertFrom(typeof(string))); } [TestMethod] public void ObjectFromInt() { Assert.IsTrue(converter.CanConvertFrom(typeof(int))); } [TestMethod] public void ObjectFromDouble() { Assert.IsTrue(converter.CanConvertFrom(typeof(double))); } [TestMethod] public void ObjectFromDateTime() { Assert.IsTrue(converter.CanConvertFrom(typeof(DateTime))); } [TestMethod] public void StringToObject() { var stringConverter = TypeDescriptor.GetConverter(typeof(string)); Assert.IsTrue(stringConverter.CanConvertTo(typeof(object))); } [TestMethod] public void IntToObject() { var intConverter = TypeDescriptor.GetConverter(typeof(int)); Assert.IsTrue(intConverter.CanConvertTo(typeof(object))); } [TestMethod] public void DoubleToObject() { var doubleConverter = TypeDescriptor.GetConverter(typeof(double)); Assert.IsTrue(doubleConverter.CanConvertTo(typeof(object))); } [TestMethod] public void DataTimeToObject() { var objectConverter = TypeDescriptor.GetConverter(typeof(DateTime)); Assert.IsTrue(objectConverter.CanConvertTo(typeof(object))); }

 

You might think that type converters could convert any type to an object, but you'd be wrong.  Value types or reference types, it doesn't matter.  All of these tests return false.  Neither the object type converter nor any of the other type converters know how to do this conversion.  I've asked Bill why this is the case, and I'll have a new post on why this is true as soon as we know.  Until then, I say that default TypeConverters aren't very smart.

Somebody call the Orkin man...

...because it looks like I just ran into a bug.  A compiler bug.  A while back I posted about Assert.ExpectedException, and how that style of exception testing is an improvement over the ExpectedException style.  Here's the code as a reminder:

 

public static void Throws<exceptionType>(Action blockToExecute) where exceptionType : SystemException
{
    try
    {
        blockToExecute();
    }
    catch (exceptionType)
    {
        return;
    }
    catch (Exception ex)
    {
        Assert.Fail("Expected exception of type " + typeof(exceptionType) + " but type of " + ex.GetType() + " was thrown instead.");
    }

    Assert.Fail("Expected exception of type " + typeof(exceptionType) + " but no exception was thrown.");
}

Simple, right?

Not exactly.  One would think that the following test would pass:

[TestMethod]
public void SimpleTest()
{
   int[] tmp = new int[] { 0, 1, 2, 3 };

   MyAssert.Throws<IndexOutOfRangeException>(() => { tmp[100] = 100; });
}
 
Strangely, this fails with the following error message:

"Assert.Fail failed. Expected exception of type System.IndexOutOfRangeException but type of System.IndexOutOfRangeException was thrown instead."

What?  Stepping through the code did no good either.  A runtime check in the immediate window showed that the type of the exception actually did match the type of the generic parameter.  In other words, this returned true at runtime:

ex.GetType() == typeof(exceptionType) 

Baffling.  A look at the IL code via Reflector with resident C# guru Bill Wagner turned up nothing suspicious.  Oddly enough, commenting out the final catch caused my simple test to pass.

 

The only solution we had was that this must be a JITer bug...and not the dancing kind! *groan*

 

Thoughts?