Saturday, May 21, 2011

Checking for anonymous types

Because I blogged about anonymous types last month, I thought following method would also make an interesting post.

private static bool IsAnonymousType(Type type) {
    Debug.Assert(type != null, "Type should not be null");
 
    // HACK: The only way to detect anonymous types right now.
    return Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false)
               && type.IsGenericType && type.Name.Contains("AnonymousType")
               && (type.Name.StartsWith("<>", StringComparison.OrdinalIgnoreCase) ||
                   type.Name.StartsWith("VB$", StringComparison.OrdinalIgnoreCase))
               && (type.Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic;
}

For a type to be anonymous:
  • It should be marked with the CompilerGenerated attribute
  • It should be a generic type
  • Its name should contain "AnonymousType"
  • Its name should start with "<>" or "VB$"
  • It shouldn't be publicly accessible
A little fun fact is that the VB and C# compiler generate different type names. The C# compiler makes the type name start with "<>" and the VB compiler uses "VB$". Both smart safeguards, because the compiler doesn't allow us to use "<>" or "$" while defining type names. I find the C# way a tad more elegant though.

I stumbled upon this beauty while browsing the ASP.NET MVC source (System.Web.Helpers.ObjectVisitor). Because there is no direct way to detect anonymous types yet, I'm pretty sure this is the best implementation out there.

9 comments:

  1. Interesting. One thing I'm curious about, is there a missing || between the <> and VB# checks?

    Thanks for sharing.

    ReplyDelete
  2. Am I missing something, or there's an OR operator missing, when checking if the type name starts with "<>" or "VB$" ?

    ReplyDelete
  3. Nice catch, must have accidentally removed it formatting the code snippet. Fixed!

    ReplyDelete
  4. um... why would I want to do this?

    ReplyDelete
  5. They use it to print friendlier type names in HTML tags.

    ReplyDelete
  6. Since we have to pass through every clause to determine if it IS an anonomous type, and therefore, any failure along the way means it definitely isn't, so we can take advantage C#'s if()'s "early-out" by putting the fastest comparisons first.

    The "is type.IsGenericType" is probably the fastest (and will also immediately eliminate most classes). The test for non-public is probably the next fastest.

    After that, looking at the start of a string is faster that scanning the whole thing, but I have no idea where checking for an attribute comes in sped-wise (I'm gonna guess it's slow).

    Putting all that together, we'd get:

    return
    type.IsGenericType
    && (type.Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic
    && (type.Name.StartsWith("<>", StringComparison.OrdinalIgnoreCase)
    || type.Name.StartsWith("VB$", StringComparison.OrdinalIgnoreCase))
    && type.Name.Contains("AnonymousType")
    && Attribute.IsDefined(type, typeof (CompilerGeneratedAttribute), false);

    ReplyDelete
  7. I have also noticed that Anonymous types have a null namespace. Is there any reason why a type other than an anonymous type would have a null namespace? That is what I am using to detect them, but now I wonder if that is not entirely correct?

    ReplyDelete
  8. Did you read this thread: http://stackoverflow.com/questions/4603139/a-c-sharp-class-with-a-null-namespace?

    ReplyDelete