This chapter describes the mapping between .NET objects and the Caché proxy classes that represent the .NET objects.
Only classes, methods, and fields marked as
This chapter describes mappings of the following types:
Assembly and class names are preserved when imported, except that each underscore (_) in an original .NET class name is replaced with the character
uand each dollar sign ($) is replaced with the character
din the Caché proxy class name. Both the
dare case-sensitive (lowercase).
Primitive types and primitive wrappers map from .NET to Caché as shown in the following table.
The result of importing a .NET class is an ObjectScript abstract class. For each .NET property that does not already have corresponding getter and setter methods (imported as is), the .NET Gateway engine generates corresponding Object Script getter and setter methods. It generates Setters as setXXX, and getters as getXXX, where XXX is the property name. For example, importing a .NET String property called Name results in a getter method getName() and a setter method setName(%Library.String). The Gateway also generates set and get class methods for all static members.
After you perform the .NET Gateway import operation, all methods in the resulting Caché proxy class have the same name as their .NET counterparts, subject to the limitations described in the Method Names section. They also have the same number of arguments. The type for all the Caché proxy methods is %Library.ObjectHandle(). The .NET Gateway engine resolves types at runtime.
For example, the .NET method test:
public boolean checkAddress(Person person, Address address)
is imported as:
Method checkAddress(p0 As %Library.ObjectHandle, p1 As %Library.ObjectHandle) As %Library.ObjectHandle
While Caché Basic and ObjectScript do not support overloading, you can still map overloaded .NET methods to Caché proxy classes. This is supported through a combination of largest method cardinality and default arguments. For example, if you are importing an overloaded .NET method whose different versions take two, four, and five arguments, there is only one corresponding method on the Caché side; that method takes five arguments, all of %ObjectHandle type. You can then invoke the method on the Caché side with two, four, or five arguments. The .NET Gateway engine then tries to dispatch to the right version of the corresponding .NET method.
While this scheme works reasonably well, avoid using overloaded methods with the same number of arguments of similar types. For example, the .NET Gateway has no problems resolving the following methods:
test(int i, string s, float f) test(Person p) test(Person p, string s, float f) test(int i)
However, avoid the following:
test(int i) test(float f) test(boolean b) test(object o)
For better results using the .NET Gateway, use overloaded .NET methods only when absolutely necessary.
Caché has a limit of 31 characters for method names. Ensure your .NET method names are not longer than 31 characters. If the name length is over the limit, the corresponding Caché proxy method name contains only the first 31 characters of your .NET method name. For example, if you have the following methods in .NET:
thisDotNetMethodHasAVeryLongName(int i) // 32 characters long thisDotNetMethodHasAVeryLongNameLength(int i) // 38 characters long
Caché imports only one method with the following name:
thisDotNetMethodHasAVeryLongNam // 31 characters long
The .NET reflection engine imports the first one it encounters. To find out which method is imported, you can check the Caché proxy class code. Better yet, ensure that logging is turned on before the import operation. The .NET Gateway log file contains warnings of all method names that were truncated or not imported for any reason.
Each underscore (_) in an original method name is replaced with the character
uand each dollar sign ($) is replaced with the character
d. Both the
dare case-sensitive (lowercase). If these conventions cause an unintended overlap with another method name that already exists on the Caché side, the method is not imported.
Finally, Caché class code is not case-sensitive. So, if two .NET method names differ only in case, Caché only imports one of the methods and writes the appropriate warnings in the log file.
Caché projects .NET static methods as class methods in the Caché proxy classes. To invoke them from ObjectScript, use the following syntax:
// calls static .NET method staticMethodName(par1,par2,...) Do ##class(className).staticMethodName(gateway,par1,par2,...)
You invoke .NET constructors by calling %New(). The signature of %New() is exactly the same as the signature of the corresponding .NET constructor, with the addition of one argument in position one: an instance of the .NET Gateway. The first thing %New() does is to associate the proxy instance with the provided Gateway instance. It then calls the corresponding .NET constructor. For example:
// calls Student(int id, String name) .NET constructor Set Student=##class(gateway.Student).%New(Gateway,29,"John Doe")
The .NET Gateway projects and imports .NET static final variables (constants) as Final Parameters. It preserves the names when imported, except that it replaces each underscore (_) with the character
uand each dollar sign ($) with the character
d. Both the
dare case-sensitive (lowercase).
For example, the following static final variable:
public const int DOTNET_CONSTANT = 1;
is mapped in ObjectScript as:
Parameter DOTNETuCONSTANT As INTEGER = 1;
From ObjectScript, access the parameter as:
The .NET Gateway supports passing parameters by reference, by supporting the .NET OUT and REF parameters. Only objects may be used as OUT and REF parameters; scalar values are not supported. For this convention to work, you must preallocate a temporary object of the corresponding type. Then call the method and pass that object by reference. The following are some examples:
public void getAddressAsReference(out Address address)
To call this method from ObjectScript, create a temporary object; there is no need to set its value. Then call the method and pass the OUT parameter by reference, as follows:
Set tempAddress=##class(remote.test.Address).%New(gateway) Do student.getAddressAsReference(.tempAddress)
The following example returns an array of Address objects as an OUT parameter:
void getOldAddresses(out Address address)
To call the previous method from ObjectScript, use the following code:
Set oldAddresses=##class(%ListOfObjects).%New(gateway) Do person.getOldAddresses(.oldAddresses)
Arrays of primitive types and wrappers are mapped as %Library.ListOfDataTypes. Arrays of object types are mapped as %Library.ListOfObjects. Only one level of subscripts is supported.
The Gateway projects .NET byte arrays (byte) as %Library.GlobalBinaryStream. Similarly, it projects .NET char arrays (char) as %Library.GlobalCharacterStream. This allows for a more efficient handling of byte and character arrays.
You can pass byte and stream arrays either by value or by reference. Passing by reference allows changes to the byte or character stream on the .NET side visible on the Caché side as well. For example, using the following:
System.Net.Sockets.Stream.Read(byte buffer, int offset, int size)
byte buffer = new byte[maxLen]; int bytesRead = inputStream.Read(buffer,offset,maxLen);
The equivalent code in ObjectScript:
Set readStream=##class(%GlobalBinaryStream).%New() // we need to 'reserve' a number of bytes since we are passing the stream // by reference (DotNet's equivalent is byte ba = new byte[max];) For i=1:1:50 Do readStream.Write("0") Set bytesRead=test.read(.readStream,50) Write readStream.Read(bytesRead)
The following example passes a character stream by value, meaning that any changes to the corresponding .NET char is not reflected on the Caché side:
Set charStream=##class(%GlobalCharacterStream).%New() Do charStream.Write("Global character stream") Do test.setCharArray(charStream)
ObjectScript has limited support for recasting; namely, you can recast only at a point of a method invocation. However, since all Caché proxies are abstract classes, this should be sufficient.
The .NET Gateway automatically redirects any standard .NET output in the corresponding .NET code to the calling Caché session. It collects any calls to System.out in your .NET method calls and sends them to Caché to display in the same format as you would expect to see if you ran your code from .NET. To disable this behavior and direct your output to the standard output device as designated by your .NET code (in most cases that would be the console), set the following global reference in the namespace where the session is running:
Set ^%SYS("Gateway","Remote","DisableOutputRedirect") = 1
Rather than aborting import, the .NET Gateway engine silently skips over all the members it is unable to generate. If you repeat the import step with logging turned on, Caché records all skipped members (along with the reason why they were skipped) in the
WARNINGsection of the log file.
The .NET Gateway engine always makes an attempt to preserve assembly and method names, parameter types, etc. That way, calling an Caché proxy method is almost identical to calling the corresponding method in .NET. It is therefore important to keep in mind Caché Basic and ObjectScript restrictions and limits while writing your .NET code. In a vast majority of cases, there should be no issues at all. You might run into some Caché Basic or ObjectScript limits. For example:
.NET method names should not be longer than 30 characters.
You should not have 100 or more arguments.
You should not try to pass String objects longer than 32K.
Do not rely on the fact that .NET is case-sensitive when you choose your method names.
Do not try to import a static method that overrides an instance method.
The .NET Gateway cannot generate proxy classes for .NET generic classes. It similarly cannot import .NET classes with generic subclasses or subinterfaces.
.NET Events are not supported — Caché code cannot be called from delegate notifications.