This is the fifth in my blog series on Visual Studio 2010 Designer Multi-Targeting
A few updates were made to the TypeDescriptor classes to support multi-targeting:
With the different VS project types came slightly different multi-targeting solutions. Windows and Web Forms attached the TargetFrameworkProvider to control instances on the design surface. Workflow projects took a more global solution: attaching the provider to all types. However, this broke down when trying to attach the provider to interfaces since they don’t have a common parent.
The new InterfaceType property allowed us to attach a TypeDescriptionProvider to all interfaces. This is similar to ComObjectType which allows you to attach to all COM types.
Attaching providers was slow… VERY slow. Never before had we attached so many TypeDescriptionProviders at design time – it was a true stress test.
When attaching a TypeDescriptionProvider to a type, any derived type or instance of that type also gets the custom provider. This results in a complex chaining of providers, each of which may have its own cache. When providers are added or removed, we must ensure that the cache for providers higher in the chain are refreshed. In .NET4 we optimized TypeDescriptor to avoid some cache refreshes that weren’t necessary.
Previously, TypeDescriptionProvider.GetReflectionType always returned types that were compatible with runtime types. Sometimes they were runtime types; and sometimes they were just imposters which redirected CLR APIs to the real runtime type while modifying certain reflection data. I’m glossing over things, but you get the idea.
In .NET4, the TargetFrameworkProvider introduced new reflection types which were no longer compatible with runtime types. Now that there were multiple target frameworks, types no longer fit into a single global “universe”. Now reflection types came from a 4.0 universe, or a 3.5 universe, etc. However, since the designer is still just running .NET4 we needed to add this new API to map types back into to the runtime.
The designer is always running the most recent framework, so we know that the target version is always equal or less than the runtime. Assuming that there are no breaking changes, it means that there is always a runtime type that corresponds to the target reflection type. However, when targeting a lower version there is not always a reflection type for a given runtime type (if the type was added after the target version).
Ideally TypeDescriptionProvider.GetReflectionType would just return null in this case. However this allowance was never built into pre-existing code and documentation. To avoid breaking this legacy contract we added TypeDescriptionProvider.IsSupportedType. This method returns false if a runtime type does not exist for a given reflection type.