Skip to main content

Mapping Specification

This chapter describes the mapping between .NET objects and the Caché proxy classes that represent the .NET objects.

Important:

Only classes, methods, and fields marked as public are imported.

This chapter describes mappings of the following types:

Assembly and Class Names

Assembly and class names are preserved when imported, except that each underscore (_) in an original .NET class name is replaced with the character u and each dollar sign ($) is replaced with the character d in the Caché proxy class name. Both the u and the d are case-sensitive (lowercase).

Primitives

Primitive types and primitive wrappers map from .NET to Caché as shown in the following table.

.NET Caché
bool %Library.BooleanOpens in a new tab
byte %Library.IntegerOpens in a new tab
char %Library.StringOpens in a new tab
double %Library.NumericOpens in a new tab
float %Library.DoubleOpens in a new tab
int %Library.IntegerOpens in a new tab
long %Library.IntegerOpens in a new tab
sbyte %Library.IntegerOpens in a new tab
short %Library.SmallIntOpens in a new tab
string %Library.StringOpens in a new tab
System.Boolean %Library.BooleanOpens in a new tab
System.Byte %Library.IntegerOpens in a new tab
System.Char %Library.StringOpens in a new tab
System.DateTime %Library.TimeStampOpens in a new tab
System.Double %Library.NumericOpens in a new tab
System.Int16 %Library.SmallIntOpens in a new tab
System.Int32 %Library.IntegerOpens in a new tab
System.Int64 %Library.IntegerOpens in a new tab
System.SByte %Library.IntegerOpens in a new tab
System.Single %Library.DoubleOpens in a new tab
System.String %Library.StringOpens in a new tab
System.UInt16 %Library.SmallIntOpens in a new tab
System.UInt32 %Library.IntegerOpens in a new tab
System.UInt64 %Library.IntegerOpens in a new tab
uint %Library.IntegerOpens in a new tab
ulong %Library.IntegerOpens in a new tab
ushort %Library.SmallIntOpens in a new tab

Properties

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.

Methods

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

Overloaded Methods

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 %ObjectHandleOpens in a new tab 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)

Tip:

For better results using the .NET Gateway, use overloaded .NET methods only when absolutely necessary.

Method Names

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 u and each dollar sign ($) is replaced with the character d. Both the u and the d are 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.

Static Methods

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,...)

Constructors

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")

Constants

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 u and each dollar sign ($) with the character d. Both the u and the d are 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:

##class(MyDotNetClass).%GetParameter("DOTNETuCONSTANT"))

OUT and REF Parameters

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)

.NET Arrays

Arrays of primitive types and wrappers are mapped as %Library.ListOfDataTypesOpens in a new tab. Arrays of object types are mapped as %Library.ListOfObjectsOpens in a new tab. Only one level of subscripts is supported.

The Gateway projects .NET byte arrays (byte[]) as %Library.GlobalBinaryStreamOpens in a new tab. Similarly, it projects .NET char arrays (char[]) as %Library.GlobalCharacterStreamOpens in a new tab. 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)

in .NET:

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)

Recasting

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.

.NET Standard Output Redirection

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

Restrictions

Important:

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 WARNING section 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.

For details on Caché Basic and ObjectScript naming conventions, see Variables in Using Caché ObjectScript, Naming Conventions in Using Caché Objects, Identifiers and Variables in Using Caché Basic, and Rules and Guidelines for Identifiers in the Caché Programming Orientation Guide.

FeedbackOpens in a new tab