Type.FullName returning null in the wrong situations?

Haibo Luo explained way back in 2006 the reasoning behind why Type.FullName and Type.AssemblyQualifiedName return null in certain situations. My reaction was one of surprise when I first encountered this, but it soon made sense once I read the documentation. The workaround, if all you want is a string representation of the type (strictly for display purposes only), is to call Type.ToString().

Recently I’ve been working on a feature for a future version of the Framework which is making use of a lot Reflection, in particular these properties. Unfortunately, I came across a couple of situations where these return null when they really shouldn’t.

The following shows this with an single dimensional array of Action<T>:

Type type = Type.GetType("System.Action`1[]");

Console.WriteLine("Type.FullName: " + (type.FullName ?? "null"));

Which outputs:

Type.FullName: null

Now remember, if you read Haibo’s post above, you would know that Type.FullName returns null when it cannot guarantee that Type.GetType can resolve the same type. Clearly, however, in this situation Type.GetType can round trip the result – as it what was what we used to get the Type in the first place! If you try pointers and references to a generic type definition, such as ‘Action`1*’ and ‘Action`1&’, you’ll notice the same issue.

Okay, so what’s the reason behind this? Well it turns out that the way Reflection decides whether its going to return null or not, is by doing something similar to:

public string FullName
{
    get
    {
        if (!IsGenericTypeDefinition && ContainsGenericParameters)
        {
            return null;
        }

        return GetRealFullName();
    }
}

Now with the above type, an Action<T>[] isn’t actually a generic definition itself, only its element, Action<T>, is. It is however, considered as having unresolved generic parameters, indicated by ContainsGenericParameters (which does walk into elements). Hence why the property takes the the null path.

Now the question that Reflection really should be asking instead of the IsGenericTypeDefinition check, is; ‘am I or any of my elements a generic definition?’, but because generic definitions can’t have element types, can be simply shortened to ‘am I or my root element a generic definition?’ Once, we’ve got the right question, we can now come up with a workaround.

Okay, first we need a way to figure out our root element:

private static Type GetRootElementType(Type type)
{
    Type elementType = type;
    while (elementType.HasElementType)
    {
        elementType = elementType.GetElementType();
    }

    return elementType;
}

Once we can figure out that, the rest is pretty straightforward:

private static string GetFullName(Type type)
{
    string name = type.FullName;
    if (name != null)
        return name;

    // Okay, Reflection's just told us that it can't 
    // uniquely identify this type, let us try
    Type elementType = GetRootElementType(type);
    if (!elementType.IsGenericTypeDefinition)
        return null; // Nope :(

    if (elementType.DeclaringType != null)
    {   // Handle nested classes

        return GetFullName(elementType.DeclaringType) + "+" + type.Name;
    }

    return type.Namespace + "." + type.Name;
}

Note, Type.Name does most of the work for us – it already includes any modifiers such as arrays ([]), pointers (*) and references (&). We also need to make sure that we handle nested types.

Now let’s try the original code again using our new method:

Type type = Type.GetType("System.Action`1[]");

Console.WriteLine("Type.FullName: " + (GetFullName(type) ?? "null"));

Running, outputs:

Type.FullName: System.Action`1[]

Looks good. Now for the ultimate test, the round trip:

Type type1 = Type.GetType("System.Action`1[]");

Type type2 = Type.GetType(GetFullName(type1));

Console.WriteLine("Same type? " + (type1 == type2));

Which in turn outputs:

Same Type? True

Beautiful. And there we have it – a quick any easy way to work around this. Note, although I’ve sent through through a few different types through this, I’ve haven't extensively tested it, so your results may vary. I’ll also leave it up to the reader to come up with a similar fix for Type.AssemblyQualifiedName.

I’ve already pinged the Reflection guys about this, and hopefully we’ll see a fix in a future Framework version.

Published Friday, August 21, 2009 7:00 AM by David Kean
Filed under: ,

Comments

Friday, August 21, 2009 9:26 AM by David Nelson

# re: Type.FullName returning null in the wrong situations?

Interesting write-up. I wasn't even aware of that limitation.

Sadly, based on past experience, this will probably get tossed into the "won't fix due to compatibility" bucket.

Monday, November 02, 2009 5:05 AM by erereotCewSew

# re: Type.FullName returning null in the wrong situations?

Best reviews of the day: Lexicon, Arabic lexicon, 2 dictionary lexicon quotation shakespeare vol and Greek english lexicon electronics.goodnano-av.com

Thursday, November 12, 2009 3:25 PM by tonyadamskklddrtotdfdf

# re: Type.FullName returning null in the wrong situations?

good evening  fellas. I'm actually into shoes and I was looking for that particular make. The prices as regards the velcros were approximately 300 pounds on every page. But finally I set this locate selling them for the benefit of half price. I in reality want these <a href=http://www.pradasneakers.org>Prada Sneakers</a>. I will absolutely buy those.

Sunday, November 15, 2009 5:57 AM by debt settlement

# re: Type.FullName returning null in the wrong situations?

Delicate position protect up your meet work.