Writing extension functions for .NET
On the .NET platform extension functions may be implemented in any
.NET language (the examples here assume C#).
Queries and stylesheets running on the .NET platform may also call
Java extension functions, provided the Java class is part of the standard library
implemented in the GNU Classpath DLL, or in Saxon itself. For calling conventions,
see Writing extension functions in Java
you want to write your own extensions in Java, you will need to compile them
to .NET assemblies using IKVMC.
An extension function is invoked using a name such as
The prefix must be the prefix associated with a namespace declaration that is in scope.
The namespace URI is used to identify a .NET class, and the local name is used to identify
a method, property, or constructor within the class.
The command line option -TJ is useful for debugging the loading of .NET
extensions. It gives detailed information about the methods that are examined for
a possible match.
The basic form of the namespace URI is
"clitype:" followed by the fully-qualified type name
xmlns:env="clitype:System.Environment"). This form
works for system classes and classes in a loaded assembly. If an assembly needs
to be loaded, extra information can be given in the form of query parameters. For example
The parameters that are recognized are:
The simple name of the assembly
The version number, up to four integers separated by periods
The culture (locale), for example "en-US"
The public key token of the assembly's strong name, as 16 hex digits
The location of the assembly, as a URI
The partial name of the assembly (as supplied to
from keyword is present, the other parameters are ignored. The value of
from must be a URI: if it is relative, it is relative to the current directory from
which the application is launched.
partialName keyword is present, the assembly is loaded (if possible) using
Assembly.LoadWithPartialName() and the other parameters are ignored.
Note that loading an assembly by simple name works only in limited cases: for example, if the
assembly is already loaded, or if it is in the same directory as the entry point of the application. This option works
well if the extension function is in a class contained in the same assembly as the application that fired off the
query or transformation.
If the assembly is a library DLL in the global assembly cache, use the
gacutil /l command
to list the assemblies present in the GAC, and to extract the required version, culture, and strong name attributes.
For example, suppose you want to call a static method
disappear() in class
Magic.Circle, and this class is contained in an assembly
Magic, Version=7.2.2200.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a4b, Custom=null
Then the URI you would use to identify this class is
clitype:Magic.Circle.Conjurer?asm=Magic;ver=7.2.2200.0;sn=b03f5f7f11d50a4b, and an actual call
of the function might take the form:
The rest of this section considers how a .NET method, property, or constructor is identified. This decision
(called binding) is always made at the time the XPath expression is compiled.
There are three cases to consider: static methods, constructors, and instance-level methods. In
addition, a public property in a class is treated as if it were a zero-argument method, so static properties
can be accessed in the same way as static methods, and instance-level properties
in the same way as instance-level methods. (Note that the property name is used directly: it is not
prefixed by "get".)