ReSharper DevGuide

Edit page

Type systems

The IDeclaredElement interfaces provide a semantic view of something that has a declaration, but that doesn’t necessarily allow for modelling usages of the element. Specifically, if we take a look at CLR types, a declared element is a declaration of a type, such as a class, but it can’t represent all usages of the type, such as in an array, pointer or closed generic type. The ITypeElement interface can provide a semantic view of the declaration of Foo or Bar<T>, but it can’t represent Foo[] or Bar<string>.

Declared elements need to be able to model these usage scenarios to provide information about base classes, method signatures, etc. In order to do so, the derived declared elements (such as ITypeElement) use an additional interface hierarchy to represent this “type system” information. This hierarchy is language specific, like the derived declared elements that use them. For CLR types, it’s the IType hierarchy, for JavaScript, it’s IJavaScriptType.

This IType is additional information rather than a replacement for the declared element’s semantic view. While the IType can return a symbol table of all type members, it doesn’t provide accessors in the same way that ITypeElement does. However, it is possible to get back to the declared element. If the type implements IDeclaredType, the GetTypeElement method will return the ITypeElement, which gives the full semantic view of the type.

Arrays and pointers are both defined in terms of another instance of IType, allowing for recursively declaring types, such as multi-dimensional arrays, or arrays of generic types. The derived interfaces, such as a IArrayType allow getting the element type (such as int in int[]), and in the case of arrays, the array’s rank.

Generic types are internally represented by a declared element and an instance of ISubstitution, which describes how the generic type parameters are substituted. (Non generic types are represented in the same manner, but use the Null Object Pattern and use an instance of EmptySubstitution)

It’s also possible to get the underlying declared type via the IType.GetScalarType method, which returns the element type for arrays, and will downcast to IDeclaredType for other types.

Types are retrieved from a declared element - such as a base type, or member type signature. Alternatively, types can be created using the TypeFactory.CreateType methods, passing in a fully qualified type name and any type parameters, or an IDeclaredElement and an instance of ISubstitution. Some frequently used types are also available from the PredefinedType class.

Last modified: 10 July 2017