VisiBroker for .NET Developer’s Guide : IDL to C# mapping

IDL to C# mapping
This chapter describes the VisiBroker for .NET IDL-to-C# language mapping, as generated by the idl2cs code generation tool.
Names
By default, IDL names and identifiers are mapped to C# names and identifiers using mixed case. This is an optional mapping, controlled by the compiler directive -[no_]mixed_caps with mixed caps being the default.
By default, methods, attributes, and factory methods will be named so that the name begins with an initial capital letter and each logical “word” in the name also has an initial capital letter. In this context, an identifier part is considered a logical word if it is separated by an underscore (_) on either side. For example an IDL method name, foo_bar, would be mapped to FooBar in the generated C# code.
Enums and member fields of structs, exceptions, and valuetypes are mapped to names beginning with a lower case letter, but each logical “word” that follows will have an initial capital letter. For example, foo_bar would become fooBar.
There is an exception where names in all capital letters would not be translated to mixed case names in the generated C# code, nor would any intermediate underscores be collapsed. For example FOO_BAR would remain as is.
Leading and trailing underscores are preserved in all translated names. For example, _foo_bar_ becomes _FooBar_.
If a name collision with a C# keyword is generated in the mapped C# code, the name collision is resolved by prepending a commercial at (@) symbol to the mapped name. The @ prefix is a C# convention. For example, the C# keyword string would be mapped to @string, but the symbol's real name is still string when interpreted in .NET. In other .NET languages where string is not a keyword (for example, J#) the symbol is recognized as string.
In addition, because of the nature of the C# language, a single IDL construct may be mapped to several (differently named) C# constructs. The additional names are constructed by appending a descriptive suffix. For example, the IDL interface AccountManager is mapped to the C# interface AccountManager and additional C# classes AccountManagerOperations and AccountManagerHelper.
In the exceptional cases where the additional names may conflict with other mapped IDL names, the resolution rule described above is applied to the other mapped IDL names. In other words, the naming and use of required “additional” names takes precedence.
For example, an IDL interface whose name is fooHelper is mapped to C# class _fooHelper, regardless of whether an interface named foo exists. The helper class for C# class _fooHelper is named _fooHelperHelper.
IDL names that would normally be mapped unchanged to C# identifiers that conflict with C# reserved words will have the collision rule applied.
Reserved generated suffixes
The mapping reserves the use of several names for use as class suffixes. The use of any of these names for a user-defined IDL type or interface (assuming it is also a legal IDL name) will result in the mapped name having an underscore (_) prepended. The reserved generated suffix names are as follows:
Helper—The C# class <type>Helper, where <type> is the name of an IDL user-defined type
NS—The nested scope C# namespace name <interface>NS, where <interface> is the name of an IDL interface.
ValueData—The C# classes <valuetype>ValueData and <valuetype>ValueFactory where <valuetype> is the name of an IDL valuetype type.
Reserved words
The mapping reserves the use of several words for its own purposes. The use of any of these words for a user-defined IDL type or interface (assuming it is also a legal IDL name) will result in the mapped words having a commercial at (@) symbol prepended. The reserved keywords in the C# language are as follows:
Basic types
The following table shows how the defined IDL types map to basic C# types.
When there is a potential mismatch between an IDL type and its mapped C# type, a standard CORBA exception can be raised. For the most part, exceptions are in two categories,
Additional details are described in the following sections.
C# null
The C# null may only be used to represent null CORBA object references and valuetypes (including recursive valuetypes). For example, a zero length string, rather than null must be used to represent the empty string. This is also true for arrays and any constructed type, except for valuetypes. If you attempt to pass a null for a structure, it will raise a system NullReferenceException.
Boolean
The IDL type boolean is mapped to the C# type bool. The IDL constants TRUE and FALSE are mapped to the C# constants true and false.
Char
IDL characters are 8-bit quantities representing elements of a character set while C# characters are 16-bit unsigned quantities representing Unicode characters. To enforce type-safety, the CORBA runtime asserts range validity of all C# chars mapped from IDL chars when parameters are marshaled during method invocation. If the char falls outside the range defined by the character set, a CORBA::DATA_CONVERSION exception is thrown.
The IDL wchar maps to the C# char type.
String and WString
The IDL type string, both bounded and unbounded variants, is mapped to the C# type string. Range checking for characters in the string as well as bounds checking of the string are done at marshal time.
The IDL type wstring, used to represent Unicode strings, is mapped to the C# type string. Bounds checking of the string is done at marshal time.
Integer types
IDL short and unsigned short map to C# type short. IDL long and unsigned long map to C# type int.
Note
Because there is no uniform support in .NET for unsigned types, unsigned IDL types are mapped to their signed equivalents in C#. The developer is responsible for ensuring that negative integers in .NET are handled correctly as large unsigned values.
IDL type extensions
This section summarizes VisiBroker for .NET support for IDL type extensions. The first table provides a summary for quick look-ups. This is followed by a table summarizing support for new types.
1 VisiBroker for .NET might support it in a future release of the OMG standard implementation.
2 UNICODE is used “on the wire.”
Constants
Constants are mapped to a public abstract class with the same name as the constant and containing a public const int field named Value. This field holds the constant's value.
This code sample shows the mapping of an IDL constant within a module to a C# class.
/* From Example.idl: */
module Example {
const long aLongerOne = -123;
};
// Example.cs
namespace Example {
public abstract class ALongerOne {
public const int Value = (int) -123;
}
}
Note:
Constants within an interface or valuetype are put into a namespace with the NS suffix appended to the name of the interface or valuetype.
Constructed types
IDL constructed types include enum, struct, union, sequence, and array. The types sequence and array are both mapped to the C# array type. The IDL constructed types enum, struct, and union are mapped to a C# class that implements the semantics of the IDL type. The C# class generated will have the same name as the original IDL type.
Enumerations
An IDL enum is mapped to a C# enum with the same name as the enum type which declares the enum values. The code sample below is an example of an IDL enum mapped to a C# enum.
// Example.idl
module Example {
enum EnumType (first, second, third};
};
// Example.cs
public enum EnumType {
first
second
third
}
Structs
An IDL struct is mapped to a C# class with the same name that provides instance variables for the fields in IDL member ordering and a constructor for all values.
This code sample shows the mapping of an IDL struct to C#.
// Example.idl
module Example {
struct StructType {
long field1;
string field2;
};
};
// Example.cs
public sealed class StructType
public int field1;
public string field2;
public StructType() {
field2 = ““;
}
public StructType (int field1, string field2) {
this.field1 = field1;
this.field2 = field2;
}
override public string ToString() {
System.Text.StringBuilder _ret =
new System.Text.StringBuilder(“struct Example.StructType {“);
_ret.Append(“\n”);
_ret.Append(“int field1=”);
_ret.Append(field1);
_ret.Append(“,\n”);
_ret.Append(“string field2=”);
_ret.Append(“field2 != null?’\”’ + field2 + ‘\”’:null);
_ret.Append(“\n”);
_ret.Append(“}”);
return _ret.ToString();
}
override public int GetHashCode() {
returns base.GetHashCode();
}
override public bool Equals(object o) {
if(this == o) return true;
if(o == null) return false;
if(o is Example.StructType) {
Example.StructType obj = (Example.StructType) o;
bool res = true;
do {
res = this.field1 == obj.field1;
if(!res) break;
res = this.field2 == obj.field2 ||
(this.field2 != null && obj.field2 != null &&
this.field2Equals(obj.field2));
} while(false);
return res;
}
else {
return false;
}
}
}
Unions
An IDL union is mapped to a sealed C# class of the same name. It provides the following:
If there is a name clash with the mapped union type name or any of the field names, the normal name conflict resolution rule is used: prepend an underscore (_) for the discriminator.
The branch accessor and modifier methods are overloaded and named after the branch. Accessor methods will raise the CORBA::BAD_OPERATION system exception if the expected branch has not been set.
If there is more than one case label corresponding to a branch, the simple modifier method for that branch sets the discriminant to the value of the first case label. In addition, an extra modifier method which takes an explicit discriminator parameter is generated.
If the branch corresponds to the default case label, then the modifier method sets the discriminant to a value that does not match any other case labels.
It is illegal to specify a union with a default case label if the set of case labels completely covers the possible values for the discriminant. It is the responsibility of the C# code generator (for example, the IDL compiler, or other tool) to detect this situation and refuse to generate illegal code.
A default method _default() is created if there is no explicit default case label, and the set of case labels does not completely cover the possible values of the discriminant. It will set the value of the union to be an out-of-range value.
This code sample shows the mapping of an IDL union to C#.
// Example.idl
module Example {
enum EnumType { first, second, third, fourth, fifth, sixth };
union UnionType switch (EnumType) {
case first: long win;
case second: short place;
case third:
case fourth: octet show;
default: boolean other;
};
};
// Example.cs
public sealed class UnionType {
private object _object;
private Example.EnumType _disc = Example.EnumType.fifth;
internal bool _defaultState = false;

// constructor
public UnionType() {
}

// discriminator accessor
public Example.EnumType discriminator() {
return _disc;
}

// win
public int Win() { ... }
public void Win(int _vis_value) { ... }

// place
public short Place() { ... }
public void Place(short _vis_value) { ... }

// show
public byte Show() { ... }
public void Show(byte _vis_value) { ... }
public void Show(Example.EnumType disc, byte _vis_value) { ... }

// other
public bool Other() {...}
public void Other(bool _vis_value) { ... }
public void Other(Example.EnumType disc, bool _vis_value) { ... )
override public string ToString () { . . .}
overrinde public int GetHashCode() { ... }
public bool Equals(object o) { . . .}
}
Sequences and Arrays
An IDL sequence is mapped to a C# array. In the mapping, anywhere the sequence type is needed, an array of the mapped type of the sequence element is used.
An IDL array is mapped in the same way as an IDL bounded sequence. In the mapping, anywhere the array type is needed, an array of the mapped type of array element is used. In C#, the natural C# subscripting operator is applied to the mapped array. The length of the array can be made available in C#, by bounding the array with an IDL constant, which will be mapped as per the rules for constants.
The following code sample shows the mapping for an array.
// Example.idl
const long ArrayBound = 42;
typedef long larray[ArrayBound];
// Example.cs
public abstract class ArrayBound {
public const int Value = (int) 42;
}
Modules
An IDL module is mapped to a C# namespace with the same name. All IDL type declarations within the module are mapped to corresponding C# class or interface declarations within the generated namespace.
IDL declarations not enclosed in any modules are mapped into the (unnamed) C# global scope.
The code sample below shows the C# code generated for an IDL module.
// Example.idl
module Example {
...
};
// Example.cs
namespace Example {
...
}
Interfaces
Given a user-defined type named Foo, the idl2cs compiler generates the following:
There are no special “nil” object references. C# null can be passed freely wherever an object reference is expected.
Attributes are mapped to a pair of C# accessor and modifier methods. These methods have the same name as the IDL attribute and are overloaded. There is no modifier method for IDL “readonly” attributes.
This code sample shows the mapping of an IDL interface to C#.
// Example.idl
module Example {
interface Foo {
long method(in long arg) raises(AnException);
attribute long assignable;
readonly attribute long nonassignable;
};
};
// Example.cs
namespace Example {
public sealed class FooHelper { ... }
public interface Foo : CORBA.Object, Example.FooOperations {
}
public interface FooOperations {
int Method(in long arg) throws Example.AnException;
int Assignable();
void Assignable(int assignable);
int Nonassignable ();
}
public class _FooStub : CORBA.ObjectIml, Example.Foo { ... }
}
Signature and Operations interfaces
In the example above, the two interfaces, Foo and FooOperations, provide the complete signature of your IDL interface when mapped to C#. The signature interface defines the signature for each interface you declare in your IDL file, while the Operations interface provides the implementation details.
The Operations interface contains only the operations and attributes declared in the IDL interfaces. The C# Operations interface contains the mapped operation signatures. Methods can be invoked on an object reference to this interface.
Helper classes
A Helper class is provided for most of the classes in the CORBA namespace. They are also generated by the idl2cs compiler for user-defined types and are given the name of the class that is generated for the type with an additional Helper suffix. The reason for using the Helper class is to avoid loading the methods that the class offers if they are not needed. Several static methods needed to manipulate the type are supplied.
Any insert and extract operations for the type
The Helper class declares a static narrow method that allows an instance of CORBA.Object to be narrowed to the object reference of a more specific type. The IDL exception CORBA::BAD_PARAM is thrown if the narrow fails because the object reference doesn't support the request type. A different system exception is raised to indicate other kinds of errors. Trying to narrow a null will always succeed with a return value of null.
For objects like mapped structures, enumerations, unions, exceptions, valuetypes, and valueboxes, the Helper class provides methods for reading and writing the object to a stream and returning the object's repository identifier. The Helper classes generated for interfaces contain additional methods, like bind and narrow.
Methods for all Helper classes
The following methods appear in all generated Helper classes.
public static <interface_name> Extract(CORBA.Any any)
This method extracts the type from the specified Any object.
public static void Insert(CORBA.Any any, <type_name> _vis_value)
This method insert a type into the specified Any object.
public static <type_name> Read(CORBA.InputStream _input)
This method reads a type from the specified input stream.
public static CORBA.TypeCode GetTypeCode()
This method returns the TypeCode associated with this object.
public static void Write(CORBA.OutputStream _output, <type_name> _vis_value)
This method writes a type to the specified output stream.
Methods generated for interfaces
public static <interface_name> Bind()
This method attempts to bind to any instance of an object of type <interface_name>.
public static <interface_name> Bind(string name)
This method attempts to bind to an object of type <interface_name> that has the specified instance name.
public static <interface_name> Bind(string name, string host,
CORBA.Visi.BindOptions
options)
This method attempts to bind to an object of type <interface_name> that has the specified instance name and which is located on the specified host, using the specified BindOptions.
public static <interface_name> Narrow(CORBA.Object obj)
This method attempts to narrow a CORBA.Object reference to an object of type <interface_name>. If the object reference cannot be narrowed, a null value is returned.
Generated stub classes
A stub class is generated by the idl2cs compiler to provide a stub implementation for <interface_name> which the client calls. This class provides the implementation for transparently acting on an object implementation.
 
Abstract interfaces
An IDL abstract interface is mapped into a single public C# interface with the same name as the IDL interface. The mapping rules are similar to the rules for generating the C# operations interface for a non-abstract IDL interface. However, this interface also serves as the signature interface.
The mapped C# interface has the same name as the IDL interface and is also used as the signature type in method declarations when interfaces of the specified types are used in other interfaces. It contains the methods which are the mapped operations signatures.
Passing parameters
IDL parameters are mapped to normal C# actual parameters. The results of IDL operations are returned as the result of the corresponding C# method.
This code sample shows the IDL parameter mapping to C#.
// Example.idl
module Example {
interface Modes {
long operation(in long inArg, out long outArg, inout long inoutArg);
};
};
// Example.cs
namespace Example;
public interface Modes : CORBA.Object, Example.ModesOperations {
}
public interface ModesOperations {
int Operation(int inArg, out int outArg, ref int inoutArg);
}
Interface scope
OMG IDL to C# mapping specification does not allow declarations to be nested within an interface scope, nor does it allow namespaces and interfaces to have the same name. Accordingly, interface scope is mapped to a package with the same name with an NS suffix.
Mapping for exceptions
IDL exceptions are mapped very similarly to structs. They are mapped to a C# class that provides instance variables for the fields of the exception and constructors.
CORBA system exceptions are unchecked exceptions. They inherit (indirectly) from RuntimeException.
User defined exceptions are checked exceptions. They inherit (indirectly) from Exception.
User-defined exceptions
User-defined exceptions are mapped to C# classes that extend CORBA.UserException and are otherwise mapped just like the IDL struct type, including the generation of Helper classes.
If the exception is defined within a nested IDL scope (essentially within an interface or valuetype) then its C# class name is defined within a special namespace whose name is the name of the containing interface or valuetype with an NS suffix appended. Otherwise its C# class name is defined within the scope of the C# namespace that corresponds to the exception's enclosing IDL module.
This code sample shows the mapping of user-defined exceptions.
// Example.idl
module Example {
exception AnException {
string reason;
};
};
// Example.cs
namespace Example {
public sealed class AnExceptionHelper : CORBA.Streamable { ... }
 
public sealed class AnException : CORBA.UserException {
public string reason;
public AnException() : base(Example.AnExceptionHelper.GetRepID()) {
}
public AnException(string reason) : this() {
this.reason = reason;
}
public AnException (string _reason, string reason) :
base(Example.AnExceptionHelper.GetRepId() + ‘ ‘ + _reason) {
this.reason = reason;
}
override public string ToString() { . . . }
override public GetHashCode() { ... }
override public bool Equals(object o) { ... }
}
}
System exceptions
The standard IDL system exceptions are mapped to final C# classes that extend CORBA.SystemException and provide access to the IDL major and minor exception code, as well as a string describing the reason for the exception. There are no public constructors for CORBA.SystemException; only classes that extend it can be instantiated.
The C# class name for each standard IDL exception is the same as its IDL name and is declared to be in the CORBA namespace. The default constructor supplies 0 for the minor code, COMPLETED_NO for the completion code, and the empty string (“”) for the reason string. There is also a constructor which takes the reason and uses defaults for the other fields, as well as one which requires all three parameters to be specified.
Mapping for the Any type
The IDL type Any maps to the C# class CORBA.Any. This class has all the necessary methods to insert and extract instances of predefined types. If the extraction operations have a mismatched type, the CORBA::BAD_OPERATION exception is thrown.
In addition, insert and extract methods are defined to provide a high speed interface for use by portable stubs and skeletons. There is an insert and extract method defined for each primitive IDL type as well as a pair for a generic streamable to handle the case of non-primitive IDL types.
The insert operations set the specified value and reset the Any's type if necessary.
Setting the typecode via the type() accessor wipes out the value. An attempt to extract before the value is set will result in a CORBA::BAD_OPERATION exception being raised. This operation is provided primarily so that the type may be set properly for IDL out parameters.
Mapping for certain nested types
IDL allows type declarations nested within interfaces. C# does not allow classes to be nested within interfaces. Hence those IDL types that map to C# classes and that are declared within the scope of an interface must appear in a special “scope” namespace when mapped to C#.
IDL interfaces that contain these type declarations generate a scope namespace to contain the mapped C# class declarations. The scope namespace name is constructed by appending NS to the IDL type name.
This code sample shows the mapping for certain nested types.
// Example.idl
module Example {
interface Foo {
exception e1 {};
};
};
// Example.cs
namespace Example
public sealed class FooHelper { ... }
public interface Foo : CORBA.Object, Example.FooOperations {
}
public interface FooOperations {
}
namespace FooNS {
public sealed class e1Helper : CORBA.Streamable { ... }
public sealed class e1 : CORBA.UserException { ... }
}
public class _FooStub : CORBAObjectIpml, Example.Foo { ... }
}
Mapping for TypeDef
IDL types that are mapped to simple C# types may not be subclassed in C#. Therefore, any typedefs that are type declarations for simple types are mapped to the original (mapped type) any where the typedef type appears. For simple types, Helper classes are generated for all typedefs.
Typedefs for non arrays and sequences are “unwound” to their original type until a simple IDL type or user-defined IDL type (of the non typedef variety) is encountered.
This code sample shows the mapping of a complex IDL typedef.
// Example.idl
module Example {
struct EmployeeName {
string firstName;
string lastName;
};
typedef EmployeeName EmployeeRecord;
};
// Example.cs
namespace Example {
public sealed class EmployeeNameHelper : CORBA.Streamable { ... }
public sealed class EmployeeName { ... }
public sealed class EmployeeRecordHelper { ... }
}