Introspection

From Xojo Documentation

Module

Gets information about a program’s structure at runtime. Classes and methods in the Introspection module have Public rather than Global scope, so references to them must include the module name.

Classes

Each class is described in its own section.

Name Super Class Description
AttributeInfo MemberInfo Provides information about an item’s attributes. Attributes are compile-time properties that are created via the Attributes Editor in the IDE. Each attribute consists of its identifier (name) and optionally a value.
ConstructorInfo MemberInfo Provides information about a datatype’s constructors. Use the GetParameters function of this class to get information about the parameters of the constructors.
MemberInfo The super class for AttributeInfo, ConstructorInfo, MethodInfo, PropertyInfo, and TypeInfo. Contains a property that contains the datatype's name.
MethodInfo MemberInfo Provides information on the methods in the datatype. Use the GetParameters function of this class to get information on the parameters of each method in the class.
ParameterInfo Provides information on the parameters of methods belonging to the class. Use the MethodInfo.GetParameters function to obtain a ParameterInfo array to get the parameter info.
PropertyInfo MemberInfo Provides information on the properties of the datatype.
TypeInfo MemberInfo The root of the introspection system and the primary way to access program metadata. It is an abstract class that describes all the attributes or members a datatype might have.


Methods
GetType

Examples

Getting a class' type name

The following example in the Action event of a PushButton in a window gets information about the class instance and displays the name of its class. See the examples for the AttributeInfo, ConstructorInfo, MemberInfo, MethodInfo, PropertyInfo, TypeInfo, and ParameterInfo for examples of those classes.

Var tcp As New TCPSocket
Var t As Introspection.TypeInfo
t = Introspection.GetType(tcp)

MessageBox("My class name is " + t.Name)

Exception handling optimization

The following example shows how Introspection can massively simplify code, reduce the size of an app, and obviate the need to manually update the code when new exceptions are added to the language:

To enable error reporting one can:

• Add a window win_ReportError

• Add a module Module_ErrorHandling with function: HandleException(error as runtimeexception, source as string) As boolean

Normally one could just read the stack trace for error reporting, however as the stack on Macs does not have the required information on where the error occurred you need to put in a bit more work to get this info.

In each method or event call the HandleException function with:

Exception err

If Not Module_ErrorHandling.HandleException(err, CurrentMethodName) Then // Specifies location
// calls the HandleException method and passes it the error type
// and the location of where the error happened
// if that doesn't work then
Raise err
End If

Now instead of writing this code:

Function HandleException(error As RuntimeException, source As String) As Boolean
Var message As String

If error IsA FunctionNotFoundException Then
message = "IllegalCastException in " + source

ElseIf error IsA IllegalCastException Then
message = "IllegalCastException in " + source

ElseIf error IsA IllegalLockingException Then
message = "IllegalLockingException in " + source

ElseIf error IsA InvalidParentException Then
message = "Unhandled InvalidParentException error in "+ source

ElseIf error IsA KeyChainException Then
message = "KeyChainException in " + source

ElseIf error IsA KeyNotFoundException Then
message = "KeyNotFoundException in " + source

ElseIf error IsA NilObjectException Then
message = "NilObjectException in " + source

ElseIf error IsA NoOpenTransportException Then
message = "Unhandled NoOpenTransportException error in" + source

ElseIf error IsA OLEException Then
message = "Unhandled OLEException error in " + source

ElseIf error IsA OutOfBoundsException Then
message = "OutOfBoundsException in " + source

ElseIf error IsA OutOfMemoryException Then
message = "OutOfMemoryException in " + source

ElseIf error IsA XojoScriptAlreadyRunningException Then
message = "Unhandled XojoScriptAlreadyRunningException error in" + source

ElseIf error IsA XojoScriptException Then
message = "Unhandled XojoScriptException error in " + source

ElseIf error IsA RegExException Then
message = "RegExException in " + source

ElseIf error IsA RegExSearchPatternException Then
message = "RegExSearchPatternException in " + source

ElseIf error IsA RegistryAccessErrorException Then
message = "RegExSearchPatternException in " + source

ElseIf error IsA RuntimeException Then
message = "Unhandled RuntimeException error in " + source

ElseIf error IsA RegistryAccessErrorException Then
message = "Unhandled RegistryAccessErrorException error in " + source

ElseIf error IsA ServiceNotAvailableException Then
message = "Unhandled ServiceNotAvailableException error in " + source

ElseIf error IsA ShellNotAvailableException Then
message = "ShellNotAvailableException in " + source

ElseIf error IsA ShellNotRunningException Then
message = "ShellNotRunningException in " + source

ElseIf error IsA SpotlightException Then
message = "ShellNotRunningException in " + source

ElseIf error IsA StackOverflowException Then
message = "StackOverflowException in " + source

ElseIf error IsA ThreadAlreadyRunningException Then
message = "ThreadAlreadyRunningException in " + source

ElseIf error IsA TypeMismatchException Then
message = "TypeMismatchException in " + source

ElseIf error IsA UnsupportedFormatException Then
message = "UnsupportedFormatException in " + source

Else
Return False

End If

Var win_ReportThisError As New win_ReportError(message)
win_ReportThisError.Show

Return True

Exception
MessageBox("HandleException Error")
Return True
End Function

You can simply use Introspection and write:

Function HandleException(error As RuntimeException, source As String) As Boolean
// can use Introspection instead of if…elseif statement
Var message As String = Introspection.GetType(error).FullName + " in " + source
Var win_ReportThisError As New win_ReportError(message)
win_ReportThisError.Show

Return True

Exception

MessageBox("HandleException Error")
Return True
End Function

This not only reduces the code but also the app size — a simple demo app has:

  • with the if…elseif statement: 15.7 MB
  • comment out the two XojoScript exceptions 5.2 MB
  • use the Introspection method 4.9 MB (still works with XojoScript exceptions)

Another advantage is that whenever new Exceptions are added to the Xojo language, you don't need to add them manually to the ElseIf construct but they will be automatically dealt with as well through the generic introspection system.

Sorting an array of objects by one of their properties

Consider you have an array of objects that you like to sort:

Var dates() As Date
dates.AddRow New Date (1980, 4, 1)
dates.AddRow New Date (1912, 1, 30)
dates.AddRow New Date (1955, 10, 23)

You cannot write

dates.Sort

because the Sort method only works with simple types (Integer, String etc.).

The following method, when added to a module so that it's globally visible, can be used to accomplish this.

Simply write:

dates.SortByProperty "SQLDateTime"

By using Introspection, the method will find the "SQLDateTime" property of the Date object and then fetch its values to use with the Sortwith method.

Here is the SortByProperty method that you should put into a module:

Sub SortByProperty(Extends objs() As Object, propName As String)
// This is a convenience function for sorting an array of objects by one of their properties.
// It only works with properties of simple types such as String and Integer.
// Written by Sept 23, 2015 by Thomas Tempelmann. Use as you like.

If objs.Ubound <= 0 Then
// No sorting necessary
Return
End If

// Get the type (class) of one of the objects in the array
Var obj As Object = objs(0)
Var ti As Introspection.TypeInfo = Introspection.GetType (obj)

// Find the property by its name
Var propInfo As Introspection.PropertyInfo
For Each pi As Introspection.PropertyInfo In ti.GetProperties()
If pi.Name = propName Then
propInfo = pi
Exit
End If
Next

If propInfo = Nil Then
// Unknown property
Var exc As New KeyNotFoundException
exc.Message = "Class '" + ti.FullName + "' has no property named '" + propName + "'"
Raise exc
End If

// Fetch the values for sorting, then sort. Do this for various types
Var typeName As String = propInfo.PropertyType.FullName
If typeName = "String" Then
Var sortValues() As String
For Each obj In objs
sortValues.AddRow(propInfo.Value(obj))
Next
sortValues.SortWith(objs)

ElseIf typeName = "Int32" Then // AKA Integer
Var sortValues() As Int32
For Each obj In objs
sortValues.AddRow(propInfo.Value(obj))
Next
sortValues.SortWith(objs)

Else
// You may have to add your own cases for other sortable types if you get here

Var exc As New TypeMismatchException
exc.Message = "Property of '" + ti.FullName + "' is not of a simple sortable type"
Raise exc

End If
End Sub

See Also

ConstructorInfo, AttributeInfo, MemberInfo, MethodInfo, ParameterInfo, PropertyInfo, TypeInfo classes; GetTypeInfo function, ObjectIterator.