|Important||This document may not represent best practices for current development, links to downloads and other resources may no longer be valid. Current recommended version can be found here. ArchiveDisclaimer|
Type Descriptor Overview
The TypeDescriptor architecture enhances the capabilities of .NET reflection.
The TypeDescriptor architecture is built on the core reflection engine and adds additional rules and features. For example, the TypeDescriptor class supports merging extender properties from an IContainer, and it also supports filtering properties and events through an IDesigner.
In addition, the TypeDescriptor architecture enables several capabilities. The following table shows the architecture's capabilities.
Enables an arbitrary type to be created when another type is requested.
Enables an object's metadata to be modified.
Enables attributes to be specified dynamically.
Target substitution and shadowing
Enables one object to stand in for another.
Extended type descriptor support
Enables access to object properties added by other objects.
To support these capabilities, the TypeDescriptor class is tightly integrated with the various features of the .NET Framework component model. It is compatible with COM objects, extender providers, designers, and CLR properties.
You can use the TypeDescriptor architecture in your run-time code as well as your design-time code.
To support extensibility, the TypeDescriptor class has a companion class called TypeDescriptionProvider and a companion attribute called TypeDescriptionProviderAttribute. You can use a TypeDescriptionProviderAttribute on a class to introduce a completely different way of exposing metadata that meets your design goals.
The TypeDescriptionProvider class can be regarded as a plug-in for the TypeDescriptor class. For a particular instance of TypeDescriptor, there can be multiple type description provider classes, all offering metadata to the TypeDescriptor.
The TypeDescriptionProviderAttribute is an attribute that you can place on a class. This attribute is used to indicate that the type has a custom type-description provider associated with it. The attribute in turn provides a way, through metadata, to install a type description provider. When this type is passed to any API on the TypeDescriptor class, TypeDescriptor discovers this attribute, creates an instance of the type description provider described within it, and hooks the provider into the internal tables of TypeDescriptor. After this is done, TypeDescriptor continues processing the API. The processing allows a type to install a custom type description provider automatically on demand.
The TypeDescriptor architecture enables capabilities beyond those provided by .NET Framework reflection.
Instance substitution occurs when you want to create one type, but the type that is actually created is different from what you requested. Instance substitution is accomplished when you replace all calls to new with calls to the CreateInstance method. This method searches internal tables within TypeDescriptor for a TypeDescriptionProvider object that is associated with the given data type. If it finds one, it delegates the call to that object.
Metadata substitution occurs when you want to modify the metadata available for one or more objects. A common application of metadata substitution is in the implementation of designers. Metadata substitution can be accomplished with type description providers, which can be added and removed using the following methods on TypeDescriptor:
There are a few cases in the .NET Framework object model where the type of a property is purposely made to be non-specific. For example, the DataSource property on the DataGridView class is typed as object. This design permits the data source to accept several kinds of input, but it provides no common place to add metadata to describe the characteristics of the property. Each data-source property throughout the .NET Framework needs to have identical metadata for type converters and user interface (UI) type editors.
The AttributeProviderAttribute class addresses this situation. When this attribute is placed on a property, the rules change for obtaining attributes for the property descriptor's Attributes collection. Usually, the property descriptor gathers local attributes and merges them with attributes from the property type. When the AttributeProviderAttribute attribute is applied, the attributes are taken from the type returned from AttributeProviderAttribute, not from the actual property type. The AttributeProviderAttribute is used on data sources to point the data source’s specific type to IListSource, and the appropriate metadata is placed on IListSource to enable data binding. This redirection allows external parties such as Visual Studio to easily add metadata to all data sources.
Attributes obtained from a type declared in the AttributeProviderAttribute have a priority between the attributes of the property’s type and the attributes on the property. The full set of attributes available is the merger, in priority order, as shown in the following list:
Attribute Provider Attributes
Property Type Attributes
Target Substitution and Shadowing
Target substitution occurs when one object stands in for another. A common application of target substitution is in the implementation of designers.
In the .NET Framework designer architecture, a component can have a designer associated with it. This designer can implement IDesignerFilter and provide its own properties. These properties are merged into the property set for the component with which the designer is associated. These properties can be new to the component. They can also have the same name and type as properties already defined on the component. When the new property shares the name and type as an existing property, it is called shadowing, because the designer hides, or shadows, the existing property on the component. The following illustration shows the shadowing of a property.
Here, the component offers two properties, and the designer also offers two properties. The Text property is offered on both the designer and the component, and is being shadowed. The final result of a call to GetProperties is three properties. One exists on the component, and the other two exist on the designer.
This property filtering is accomplished through the use of ITypeDescriptorFilterService, which the design surface implements. TypeDescriptor capabilities are required when it is time to set a value on the property. The code to set a value on the Grid property would look like this:
The actual type information about the property points it to an instance of the designer, not the component. If a reflection call were made to actually set the property, the call would raise a target invocation exception because the component instance does not match the designer type.
The TypeDescriptor class has inherent logic to work around this situation. When a property call is made, the TypeDescriptor class checks to see if the member type is an instance of the object passed. If so, it lets the call proceed. If not, the class tries to locate the designer for the object, and if the designer can be found and is of the correct type, the class replaces the component instance with the designer instance.
The following methods on TypeDescriptor support target substitution:
Extended Type Descriptor Support
The GetExtendedTypeDescriptor method returns an extended custom type descriptor for the given object. An extended type descriptor is a custom type descriptor that offers properties that other objects have added to this object but are not actually defined on the object. For example, in the .NET Framework component model, objects that implement the IExtenderProvider interface can attach properties to other objects that reside in the same IContainer. The GetTypeDescriptor method does not return a type descriptor that provides these extra extended properties, but GetExtendedTypeDescriptor returns the set of these extended properties. The TypeDescriptor class automatically merges the results of these two property collections.
Although the .NET Framework component model only supports extended properties, GetExtendedTypeDescriptor can be used for extended attributes and events as well, if the type description provider supports it.