Calling .NET from Mathematica
Introduction
.NET/Link provides
Mathematica users with the ability to interact with arbitrary .NET types directly from
Mathematica. You can create objects and call methods and properties directly in the
Mathematica language. You do not need to write any .NET code, or prepare in any way the .NET types you want to use. You also do not need to know anything about
MathLink. In effect, all of .NET becomes a transparent extension to
Mathematica, almost as if every existing and future .NET type were written in the
Mathematica language itself.
We call this facility "installable .NET" because it generalizes the ability that
Mathematica has always had to plug in extensions written in other languages through the
Install function. Compared to other languages like C or C++, however, .NET
/Link makes the intermediate steps go away completely, which is why we say that .NET becomes a
transparent extension to
Mathematica.
Although .NET is sometimes referred to as an interpreted environment, this is really a misnomer. To use .NET you must write a complete program in a language like C#, compile it, and then execute it.
Mathematica users have the luxury of working in a true interpreted, interactive environment that lets them experiment with functions and build and test programs a line at a time.
.NET/Link brings this same productive environment to .NET programmers. You could say that
Mathematica becomes a scripting language for .NET.
To
Mathematica users, then, the "installable .NET" feature of
.NET/Link opens up the universe of .NET types as an extension to
Mathematica; for .NET users, it allows the extraordinarily powerful and versatile
Mathematica environment to be used as a shell for interactively developing, experimenting with, and testing .NET programs.
This guide discusses calling from
Mathematica into the .NET runtime. You will see how to load .NET assemblies and types into
Mathematica, create objects of these types, call methods and properties, and so on. You will also learn how to use
.NET/Link to call COM objects as well as standard C-style DLL functions.
Loading the NETLink` Package
You must load the
.NET/Link package before you can use
.NET/Link.
Launching the .NET Runtime
The
InstallNET function is used to launch the .NET runtime.
If you are actively developing .NET classes and other types to use in
Mathematica, you will need to restart the .NET runtime before you can reload a modified version of a class. Use the
ReinstallNET function to quit and restart the .NET runtime. Most users will have no need to ever quit or restart .NET and should avoid calling
ReinstallNET or
UninstallNET. Remember that the .NET runtime is shared by potentially many programs in your
Mathematica session. Shutting down or restarting the .NET runtime could have unexpected consequences for those programs.
| InstallNET[] | launch the .NET runtime and prepare it for use from Mathematica |
| ReinstallNET[] | quit and restart the .NET runtime |
| NETLink[] | give the LinkObject that is being used to communicate with the .NET runtime |
Launching the .NET runtime.
Loading .NET Assemblies and Types
.NET Assemblies
Programs and libraries for .NET are packaged into units called
assemblies. An assembly can be defined as a versioned, self-describing binary (DLL or EXE) containing a collection of types (classes, interfaces, structs, and so on) and optional resources. An assembly can span multiple files (a
multi-file assembly), or a single file can contain more than one assembly, but in the typical case an assembly consists of a single DLL or EXE file. Although .NET assemblies can have the DLL extension, internally they are quite different from old C-style DLLs. Conceptually, though, a .NET DLL is similar to a C-style DLL in that they are both libraries of code intended to be loaded and called by other programs. An EXE assembly is an executable program that can be launched directly, but it also can export types like a DLL for use by other programs.
Assemblies can be located anywhere on your system. The .NET Framework maintains a special location on your system where assemblies can be stored so that they can easily be found by all .NET programs on the system. This location is called the Global Assembly Cache (GAC), and is found in the
assembly subdirectory of your root Windows directory. The assemblies that are part of the .NET Framework itself are located in the GAC, and many .NET programs that you install will put assemblies there. There is no requirement that an assembly be placed into the GAC, and
.NET/Link can use assemblies located anywhere on your system.
Because all .NET types are packaged into assemblies, to load a type into
Mathematica you first need to load its assembly. You use the
LoadNETAssembly function to load an assembly into
.NET/Link. Assemblies can be loaded by specifying various types of information about the assembly, such as a full path to the assembly file, or by a full or partial name of the assembly. Assembly names will be discussed in more detail later, but for now it suffices to say that assembly names are assigned by their creators, and may bear no resemblance to the name of the actual assembly file. An example of a simple assembly name is
System.XML, which is the assembly in the .NET Framework that handles XML-related functionality. Assemblies are often named after the most important namespace they define. The actual file name of the
System.XML assembly is
System.XML.dll, and it is located somewhere nested deep down inside the GAC.
.NET/Link does not need to be explicitly instructed to load all assemblies. It will automatically load any of the .NET Framework assemblies, meaning all of the assemblies containing types whose names start with
System. (e.g.,
System.Windows.Forms.Form,
System.Drawing.Rectangle,
System.Data.DataSet, and so on). Most of the types that you will use in
.NET/Link programming are found in the system assemblies. You will have to manually load other assemblies. The
LoadNETAssembly function, described later in this tutorial, is the
Mathematica function you use to load assemblies into
.NET/Link and prepare them to be used from
Mathematica.
.NET Types
A
type is the fundamental unit of .NET programming. Every type falls into one of the following categories: classes, interfaces, structs ("value types"), enumerations, and delegates. Every object in .NET is an instance of some type. Although the set of types is broader than just classes, you might find it easier to think of types as being classes.
Types are defined in assemblies. To load and use a type in
.NET/Link, you must first load the assembly in which it resides and then load the type itself. Often these steps can be combined into a single operation.
LoadNETType, described later , is the
Mathematica function that you use to load types into
.NET/Link so they can be used from
Mathematica.
LoadNETAssembly
LoadNETAssembly is the function you use to load assemblies into
.NET/Link so that the types they contain can be used from
Mathematica.
LoadNETAssembly has a number of different argument sequences. Although all these different possible arguments might seem confusing, the basic principle is to allow virtually any way of specifying enough information about the assembly so that
.NET/Link can locate it.
| LoadNETAssembly[assemblyName] | load the specified assembly based on its name, such as "System.Web" |
| LoadNETAssembly[path] | load the assembly based on its full file path |
| LoadNETAssembly[url] | load the assembly pointed to by this URL |
| LoadNETAssembly[assemblyName,dir] | load the assembly based on its name and the directory in which it resides |
| LoadNETAssembly[assemblyName,context`] | load the assembly based on its name and the application context in which it resides
|
| LoadNETAssembly[dir] | load all the assemblies in this directory |
| LoadNETAssembly[context`] | load all the assemblies in this application context |
Loading assemblies.
Here is an example of using
LoadNETAssembly to load an assembly that is part of the .NET Framework.
| Out[3]= |  |
The return value of
LoadNETAssembly is a
NETAssembly expression. This is not a .NET object itself, just a special expression that can be used in
.NET/Link to refer to a loaded assembly in various functions that take an assembly specification as an argument.
The name used in the previous example is the
simple name of the assembly. The actual
full name, or
display name, of the assembly is longer and contains version information, among other things. You can use the full name if you want to force a certain version to be loaded (note that if you execute this on your machine, it will fail unless you have exactly the same version of the .NET Framework installed).
| Out[4]= |  |
We were able to load this assembly based on only its name, without any location information, because it is located in the GAC.
As another example of
LoadNETAssembly, say you have obtained an assembly or created one of your own in some .NET language and want to load it into
.NET/Link to use from
Mathematica. The assembly is in the file
c:\MyProgram\Bin\Debug\MyAssembly.dll. Here is how you would load it.
Loading an assembly by specifying its path is useful for assemblies that you have created yourself.
LoadNETAssembly can also load assemblies from an
assembly subdirectory in a
Mathematica application directory. This is intended for developers who are creating applications that use
.NET/Link. If you have a
Mathematica application directory called
MyApp, and it is installed into one of the standard locations for
Mathematica applications (e.g.,
<Mathematica dir>\AddOns\Applications), you can give the
MyApp directory a subdirectory named
assembly and place all the extra assemblies that your application needs into the
assembly directory. Your application's
Mathematica code can then load one of these application assemblies by supplying the assembly name and the context that corresponds to your application.
In this way, application developers can bundle private assemblies in their application layout and not require that their users perform any special installation steps such as copying assemblies into the GAC.
LoadNETType
LoadNETType is the
Mathematica function that loads .NET types so that they can be used from
Mathematica. It is often not necessary to explicitly call
LoadNETType. Whenever a .NET object is returned to
Mathematica, its type is loaded. This means that if you want to create a new object of a certain type, you can just call
NETNew and the type will be loaded when the object is returned to
Mathematica. The most common reason for calling
LoadNETType directly is if you want to use a static method or property from a type. In that case, you are not creating an object with
NETNew, so you must manually load the type.
| LoadNETType[typeName] | load the specified type |
| LoadNETType[typeName,assemblyName] | load the type from the specified assembly |
| LoadNETType[typeName,NETAssembly] | load the type from the assembly identified by a NETAssembly expression |
| LoadNETType[typeName,assemblyName,context`] | load the type from the specified assembly residing in the specified application context |
Loading types.
Here is a simple example of
LoadNETType.
| Out[5]= |  |
The return value of
LoadNETType is a
NETType expression. This is not a .NET object itself, just a special expression that can be used in
.NET/Link to refer to a .NET type in various functions that take a type specification as an argument.
Note that you must supply the full type name, including the namespace prefix (
System.Windows.Forms in this example). For the load to succeed, the assembly in which the type resides must be already loaded using
LoadNETAssembly. As mentioned earlier, assemblies for all System types are automatically loaded as needed by
.NET/Link, so there was no need to load the
System.Windows.Forms assembly manually in the previous example.
Viewing Loaded Assemblies and Types
You can use the utility functions
LoadedNETAssemblies. and
LoadedNETTypes to see what assemblies and types have been loaded into the current
Mathematica session. These are intended mainly for debugging purposes.
Viewing loaded assemblies and types.
Contexts and Visibility of Static Type Members
LoadNETType has two options that let you control the naming and visibility of static methods and fields. To understand these options, you need to understand the problems they help to solve. We have to get a bit ahead of ourselves to explain the issues, since we have not yet discussed how to call .NET methods. When a type is loaded, definitions are created in
Mathematica that allow you to call methods, properties, and fields of objects of that class. Static members are treated quite differently from nonstatic ones. Say you have a class named
MyClass in the namespace
MyCompany.Utilities, and this class contains a static method named
Foo. When you load this class, a definition must be set up for
Foo so that it can be called by name, something like
Foo[args]. The question becomes: In what context do you want the symbol
Foo defined, and do you want this context to be visible (i.e., on
$ContextPath)?
.NET/Link always creates a definition for
Foo in a context that mirrors its fully qualified classname:
MyCompany`Utilities`MyClass`Foo. This is done to avoid conflicting with symbols named
Foo that might be present in other contexts. However, you might find it clumsy to have to call
Foo by typing the full context name every time, as in
MyCompany`Utilities`MyClass`Foo[args]. The option
AllowShortContext->True (this is the default setting) causes
.NET/Link to also make definitions for
Foo accessible in a shortened context, one that consists of just the class name without the hierarchical namespace prefix. In our example, this means that you could call
Foo as simply
MyClass`Foo[args]. If you need to avoid use of the short context because there is already a context of the same name in your
Mathematica session, you can use
AllowShortContext->False. This forces all names to be put only in the "deep" context. Note that even with
AllowShortContext->True, names for statics are also put into the deep context, so you can always use the deep context to refer to a symbol if you desire.
AllowShortContext, then, lets you control the context where the symbol names are defined. The other option,
StaticsVisible, controls whether this context is made visible (put on
$ContextPath) or not. The default is
StaticsVisible->False, so you have to use a context name when referring to a symbol, as in
MyClass`Foo[args]. With
StaticsVisible->True, MyClass` will be put on
$ContextPath, so you could just write
Foo[args]. Having the default be
True would be a bit dangerous—every time you load a class a potentially large number of names would suddenly be created and made visible in your
Mathematica session, opening up the possibility for all sorts of "shadowing" problems if symbols of the same names were already present (see
Contexts for a discussion of contexts and shadowing problems).
For these reasons
StaticsVisible->True is recommended only for classes that you have written, or ones whose contents you are familiar with. In such cases, it can save you some typing, make your code more readable, and prevent the all-too-easy bug of forgetting to type the classname prefix. A classic example would be implementing the venerable "addtwo"
MathLink example program. In C#, it might look like this.
public class AddTwo {
public static int AddTwo(int i, int j) {return i + j;}
}
With the default
StaticsVisible->False, you would have to call addtwo as
AddTwo`AddTwo[3, 4]. Setting
StaticsVisible->True lets you write the more obvious
AddTwo[3, 4].
Be reminded that these options are only for
static methods and fields. As discussed later, nonstatics are handled in a way that makes context and visibility issues go away completely.
| StaticsVisible→True | make static methods and fields accessible by just their names, not in a special context |
| AllowShortContext→False | make static methods and fields accessible only in the context that mirrors the full hierarchical namespace name |
Options for LoadNETType.
Conversion of Types Between .NET and Mathematica
Before we encounter the operations of creating .NET objects and calling methods, we should examine the mapping of types between
Mathematica and .NET. When a .NET method returns a result to
Mathematica, the result is automatically converted into a
Mathematica expression. For example, .NET integer types (e.g.,
Int32,
Byte, and so on), are converted into
Mathematica integers, and .NET real number types (
Single,
Double) are converted into
Mathematica reals. The table below shows the complete set of conversions. These conversions work both ways—for example, when a
Mathematica integer is sent to a .NET method that requires a
Byte value, the integer is automatically converted to a .NET
Byte.
Note that this table gives type names as they are used in the .NET Framework. Different languages often have their own keywords that map to these underlying types. In C#, for example, the keyword
int is an alias to the
Int32 type, and in Visual Basic .NET the
Int32 type is called
Integer.
Corresponding types in .NET and Mathematica.
.NET arrays are mapped to
Mathematica lists of the appropriate depth. Thus, when you call a method that takes a
double[] (in C# notation), you might pass it
{1.0, 2.0, N[Pi], 1.23}. Similarly, a method that returns a two-deep array of integers (
int[,] in C# notation) might return to
Mathematica the expression
{{1, 2, 3}, {5, 3, 1}}.
Creating Objects
To construct .NET objects, use the
NETNew function. The first argument to
NETNew is the object's type, specified either as a
NETType expression returned from
LoadNETType or as a string giving the fully qualified type name (i.e., including the namespace prefix). If you wish to supply any arguments to the object's constructor, they follow as a sequence after the type.
| NETNew[typeName,arg1,...] | construct a new object of the specified class and return it to Mathematica |
| NETNew[NETType,arg1,...] | construct a new object of the specified class and return it to Mathematica |
Constructing .NET objects.
For example, this will create a new
Form.
| Out[6]= |  |
The return value from
NETNew is a strange expression that looks like it has the head
NETObject, except that it is enclosed in angle brackets. The angle brackets are used to indicate that the form in which the expression is displayed is quite different from its internal representation. These expressions will be referred to as
NETObject expressions.
NETObject expressions are displayed in a way that shows their type name, but you should consider them opaque, meaning that you cannot pick them apart or peer into their insides. You can only use them in
.NET/Link functions that take
NETObject expressions.
NETNew invokes a .NET constructor appropriate for the types of the arguments being passed in, and then returns to
Mathematica what is, in effect, a reference to the object. That is how you should think of
NETObject expressions—as references to .NET objects very much like object references in a .NET language like C# or Visual Basic .NET. What is returned to
Mathematica is not large no matter what type of object you are constructing. In particular, the object's data (that is, its fields) are not sent back to
Mathematica. The actual object remains on the .NET side, and
Mathematica gets a reference to it.
The previous examples specified the class by giving its name as a string. You can also use a
NETType expression, which is a special expression returned by
LoadNETType that identifies a class. When you specify the class name as a string, the class is loaded if it has not already been.
NETNew is not the only way to get a reference to a .NET object in
Mathematica. Many methods and properties return objects, and when you call such a method or property, a
NETObject expression is created. Such objects can be used in the same way as ones you explicitly construct with
NETNew.
Calling Methods, Properties, and Fields
Syntax
The
Mathematica syntax for calling .NET methods and accessing fields is very similar to the syntax used in C# and Visual Basic .NET. The box below compares the
Mathematica and C# ways of calling constructors, methods, properties, fields, static methods, static properties, and static fields. You can see that
Mathematica programs that use .NET are written in almost exactly the same way as C# (or VB .NET) programs, except
Mathematica uses [] instead of () for arguments and @ instead of the . (dot) as the "member access" operator.
An exception is that for static methods,
Mathematica uses the context mark ` in place of the dot used by C# and VB. This parallels the usage in those languages also, as their use of the dot in this circumstance is really as a scope resolution operator (like :: in C++). Although
Mathematica does not use this terminology, its scope resolution operator is the context mark. .NET namespace names map directly to
Mathematica's hierarchical contexts.
| | |
| C#: | MyClass obj = new MyClass(args); |
| Mathematica: | obj = NETNew["MyClass", args]; |
| | |
| C#: | obj.MethodName(args); |
| Mathematica: | obj@MethodName[args] |
| | |
| C#: | obj.PropertyOrFieldName = 1;
value = obj.PropertyOrFieldName; |
| Mathematica: | obj@PropertyOrFieldName = 1;
value = obj@PropertyOrFieldName; |
| | |
| C#: | MyClass.StaticMethod(args); |
| Mathematica: | MyClass`StaticMethod[args]; |
| | |
| C#: | MyClass.StaticPropertyOrField = 1;
value = MyClass.StaticPropertyOrField; |
| Mathematica: | MyClass`StaticPropertyOrField = 1;
value = MyClass`StaticPropertyOrField; |
C# and Mathematica syntax comparison.
You may already be familiar with
@ as a
Mathematica operator for applying a function to an argument:
f @ x is equivalent to the more commonly used
f[x].
.NET/Link does not usurp
@ for some special operation—it is really just normal function application slightly disguised. This means that you do not have to use @ at all. The following are equivalent ways of invoking a method.
The first form preserves the natural mapping of the syntax of most .NET languages into
Mathematica and will be used exclusively in this manual.
When you call methods, properties, or fields and get results back, .
NET/Link automatically converts arguments and results to and from their
Mathematica representations according to the table presented earlier.
In object-oriented languages, method and field names are scoped by the object on which they are called. In other words, when you write
obj.Meth(), .NET languages know that you are calling the method named
Meth that resides in
obj's class, even though there may be other methods named
Meth in other classes.
.NET/Link preserves this scoping for
Mathematica symbols so that there is never a conflict with existing symbols of the same name. When you write
obj@Meth[], there is no conflict with any other symbols named
Meth in the system—the symbol
Meth used by
Mathematica in the evaluation of this call is the one set up by
.NET/Link for this class. Here is an example using a field. First, we create a
Point object.
| Out[9]= |  |
The
Point class has fields named
X and
Y, which hold its coordinates. A user's session might also have symbols named
X or
Y in it, however. Let us set up a definition for
X that will tell us when it is evaluated.
Now set a value for the field named
X (this would be written as
pt.X = 42 in C# or VB).
You will notice that "gotcha" was not printed. There is no conflict between the symbol
X in the
Global` context that has the
Print definition and the symbol
X that is used during the evaluation of this line of code.
.NET/Link protects the names of members on the right-hand side of
@ so that they do not conflict with, or rely on, any definitions that might exist for these symbols in visible contexts.
In summary, for nonstatic methods, properties, and fields, you never have to worry about name conflicts and shadowing, no matter what context you are in or what the
$ContextPath is at the moment. This is not true for static members, however. Static methods and fields are called by their full name, without an object reference, so there is no object out front to scope the name. Here is a simple example of a static method call that invokes the .NET garbage collector. We need to call
LoadNETType before we call a static method to make sure the class has been loaded.
The name scoping issue is not usually a problem with static members because they are defined in their own contexts (
GC` in this example). These contexts are usually not on
$ContextPath, so you do not have to worry that there is a symbol of the same name in the
Global` context or in a package that has been read. If there is already a context named
GC` in your session, and it has its own function
Collect, you can always avoid a conflict by using the fully-hierarchical context name that corresponds to the full type name for a static member.
Underscores in .NET Names
.NET names can have characters in them that are not legal in
Mathematica symbols. The only common one is the underscore.
.NET/Link maps underscores in type, method, property, and field names to 'U'. Note that this mapping is only used where it is necessary—when names are used in symbolic form, not as strings. For example, assume you have a class named
My_Class. When you refer to this class name as a string, you use the underscore:
But when you call a static method in such a class, the hierarchical context name is symbolic, so you must convert the underscore to U.
The same rule applies to method and field names. To refer to such names in code, use the U. Here is how to call a property named
Some_Property.
Getting Information About Types and Objects
NETTypeInfo
It is often convenient to be able to quickly display information about the methods, properties, fields, and so on that exist in a given .NET type.
.NET/Link provides the
NETTypeInfo function to obtain this information.
| NETTypeInfo[typeName] | print information about all the members in the given type |
| NETTypeInfo[typeName,members] | print information about just the desired types of members in the given type |
| NETTypeInfo[typeName,members,"pat"] | print information about the desired types of members whose names match a string pattern |
| NETTypeInfo[obj] | print information about all the members in the object's type
|
| NETTypeInfo[NETAssembly] | print information about all the types in the given assembly |
Getting information about types and objects.
NETTypeInfo will load the type if it has not already been loaded.
This will display a lot of information about the
Process class.
The second argument to
NETTypeInfo is an optional list of the members you want to see displayed. The possible values are
"Type" (gives general info about the type itself),
"Constructors",
"Methods",
"Properties",
"Fields", and
"Events". This will show just the properties and methods.
This will show just properties with names that begin with "Peak".
The default behavior is to display the members in C# syntax. If you want to see them in Visual Basic .NET syntax, use the
LanguageSyntax option.
| | |
| LanguageSyntax | "CSharp" | the language syntax in which output should be formatted must be "CSharp" or "VisualBasic" |
| Inherited | True | whether to include inherited members |
| IgnoreCase | False | whether to ignore case in matching names to a string pattern |
Options to NETTypeInfo.
NETTypeInfo is also useful for seeing what types are in an assembly. To investigate an assembly, pass a
NETAssembly expression as the first argument. The easiest way to get a
NETAssembly expression is to call
LoadNETAssembly (even if the assembly is already loaded). The following line will show a lot of types.
When acting on a
NETAssembly, the second argument to
NETTypeInfo is an optional list of the types you want to see displayed. The possible values are
"Classes",
"Interfaces",
"Structures",
"Delegates", and
"Enums". This will show just the classes and interfaces with the word "Data" in their names.
Other Useful Functions
Utility functions for objects and types.
NETObjectQ is convenient when you need to test whether an expression is a .NET object reference. It is often used as a pattern test in function definitions.
.NET/Link uses special expressions with heads
NETType (returned by
LoadNETType) and
NETAssembly (returned by
LoadNETAssembly) to represent .NET types and assemblies in
Mathematica. As noted earlier, you can pass these expressions to functions that take types or assemblies as arguments (such as
NETNew). There are times when you might want not a
NETType expression, but an actual .NET
Type object reference for a given type, and likewise for an assembly. You can use
GetTypeObject to get the
Type object corresponding to a
NETType expression, and
GetAssemblyObject to get the .NET
Assembly object.
| Out[1]= |  |
| Out[2]= |  |
Notice above that the
NETType expression is much more informative about what type it represents than the
Type object. This is one reason why
.NET/Link uses special
NETType and
NETAssembly expressions instead of just
Type and
Assembly objects.
Once you have a
Type object, you can use methods and properties of the
Type class to learn about it.
| Out[3]= |  |
| Out[4]= |  |
Reference Counts and Memory Management
Object References in Mathematica
In our earlier treatment of
NETObject expressions we avoided discussing deeper issues such as reference counts and uniqueness. Every time a .NET object reference is returned to
Mathematica, either as a result of a method or property or an explicit call to
NETNew,
.NET/Link looks to see if a reference to this object has been sent previously in this session. If not, it creates a
NETObject expression in
Mathematica and sets up a number of definitions for it. This is a comparatively time-consuming process. If this object has already been sent to
Mathematica, in most cases
.NET/Link simply creates a
NETObject expression that is identical to the one created previously, which is a much faster operation.
There are some exceptions to this last rule, meaning that sometimes when an object is returned to
Mathematica a new and different
NETObject expression is created for it, even though this same object has previously been sent to
Mathematica. Specifically, any time an object's hash value (as determined by the object's built-in
GetHashCode() method) has changed since the last time it was seen in
Mathematica, the
NETObject expression created will be different. You do not really need to be concerned with the details of this, except to remember that
Mathematica's
SameQ function is not a valid way to compare
NETObject expressions to decide whether they refer to the same object. You must use the
SameObjectQ function.
| SameObjectQ[obj1,obj2] | return True if the NETObject expressions obj1 and obj2 refer to the same .NET object, return False otherwise |
Comparing NETObject expressions.
| Out[1]= |  |
The variable
pt refers to a .NET
Point object. Now put it into a container so you can get it back out later.
Now change the value of one of its coordinates. For a
Point object, this changes its hash value.
Now compare the
NETObject expression given by
pt and the
NETObject expression created when you ask for the first element of the
ArrayList to be returned to
Mathematica. Even though these are both references to the same .NET object, the
NETObject expressions are different. Recall that the
ArrayList class defines an indexer (in C# terminology), so you can use the
[] notation to refer to an element by index.
| Out[5]= |  |
Because you cannot use
SameQ (

) to decide whether two object references in
Mathematica refer to the same .NET object,
.NET/Link provides the
SameObjectQ function for this purpose.
| Out[6]= |  |
You may be wondering why the
SameObjectQ function is useful. Can't you just call an object's
Equals() method? It certainly gives the correct result for this example.
| Out[7]= |  |
The problem with this technique is that
Equals() does not always compare object references. Any class is free to override
Equals() to provide any desired behavior for comparing two objects of that class. Some classes make
Equals() compare the "contents" of the objects, such as the
String class, which uses it for string comparison. The function that provides the correct test is the static method
ReferenceEquals().
| Out[8]= |  |
You can think of
SameObjectQ as a convenience function that does the same thing as explicitly calling
ReferenceEquals().
In an unusual case where you need to compare object references for equality a very large number of times, the slowness of
SameObjectQ compared to
SameQ could become an issue. The only thing that could cause two
NETObject expressions that refer to the exact same .NET object to not be
SameQ is if the hash value of the object changed between the times that the two
NETObject expressions were created. If you know this has not happened, you can safely use
SameQ to test whether they refer to the same object.
ReleaseNETObject
The .NET runtime has a built-in facility called "garbage collection" for freeing up memory occupied by objects that are no longer in use by a program. Objects become eligible for garbage collection when no references to them exist anywhere, except perhaps in other objects that are also unreferenced. When an object is returned to
Mathematica, either as a result of a call to
NETNew or as the return value of a method or property, the
.NET/Link code holds a special reference to the object on the .NET side to ensure that it cannot be garbage-collected while it is in use by
Mathematica. If you know that you no longer need to use a given .NET object in your
Mathematica session, you can explicitly tell
.NET/Link to release its reference. The function that does this is
ReleaseNETObject. In addition to releasing the
Mathematica-specific reference in .NET,
ReleaseNETObject clears out internal definitions that were created in
Mathematica for the object. Any subsequent attempt to use this object in
Mathematica will fail.
| Out[9]= |  |
Now tell .NET that you no longer need to use this object from
Mathematica.
It is now an error to refer to
frm because the object's symbolic representation has been removed from the
Mathematica session. This is what you see if you try to use the released object.
| Out[11]= |  |
| ReleaseNETObject[obj] | let .NET know that you are done using obj in Mathematica |
| NETBlock[expr] | all novel .NET objects returned to Mathematica during the evaluation of expr will be released when expr finishes |
| BeginNETBlock[] | all novel .NET objects returned to Mathematica between now and the matching EndNETBlock[] will be released |
| EndNETBlock[] | release all novel objects seen since the matching BeginNETBlock[] |
| LoadedNETObjects[] | return a list of all objects that are in use in Mathematica |
Memory management functions.
Calling
ReleaseNETObject will not necessarily cause the object to be garbage-collected. It is quite possible that other references to it exist in .NET.
ReleaseNETObject does not tell .NET to throw the object away, only that it does not need to be kept around solely for
Mathematica's sake.
An important fact about the references
.NET/Link maintains for objects sent to
Mathematica is that only one reference is kept for each object, no matter how many times it is returned to
Mathematica. It is your responsibility to make sure that after you call
ReleaseNETObject, you never attempt to use that object through any reference that might exist to it in your
Mathematica session.
If you call
ReleaseNETObject[frm1], it is not the
Mathematica symbol
frm1 that is affected but the .NET object that
frm1 refers to. Therefore, using
frm2 is also an error (or any other way to refer to this same
Form object).
Calling
ReleaseNETObject is often unnecessary in casual use. If you are not making heavy use of .NET in your session, then you will not usually need to be concerned about keeping track of what objects may or may not be needed anymore—you can just let them pile up. There are times, though, when memory use in .NET will be important, and you may need the extra control that
ReleaseNETObject provides.
NETBlock
ReleaseNETObject is provided mainly for developers who are writing code for others to use. Because it is not possible to predict how code will be used, developers should always be sure that their code cleans up any unnecessary references it creates. Probably the most useful function for this is
NETBlock.
NETBlock automates the process of releasing objects encountered during the evaluation of an expression. Often, a
Mathematica program will need to create some .NET objects with
NETNew; operate with them, perhaps causing other objects to be returned to
Mathematica as the results of method calls; and finally return some result such as a number or string. Every .NET object encountered by
Mathematica during this operation is needed only during the lifetime of the program, much like the local variables provided in
Mathematica by
Block and
Module, and in C#, C++, Java, and many other languages by block scoping constructs (e.g., {}).
NETBlock allows you to mark a block of code as having the property that any new objects returned to
Mathematica during the evaluation are to be treated as temporary and released when
NETBlock finishes.
It is important to note that the preceding sentence said "new objects."
NETBlock will not cause every object encountered during the evaluation to be released—only those that are being encountered for the first time. Objects that have already been seen by
Mathematica will not be affected. This means that you do not have to worry that
NETBlock will aggressively release an object that is not truly temporary to that evaluation.
It is not enough simply to call
ReleaseNETObject on every object you create with
NETNew, because many .NET methods and properties return objects. You might not be interested in these return values. You might never assign them to a named variable because they may be chained together with other calls (as in
obj@ReturnsObject[]@Foo[]), but you still need to release them. Using
NETBlock is an easy way to be sure that all novel objects are released when a block of code finishes.
NETBlock[expr] returns whatever
expr returns.
Many
.NET/Link Mathematica programs will have the following structure.
It is very common to write a function that creates a number of
NETObject expressions and then returns one of them, the rest being temporary. To facilitate this, if the return value of a
NETBlock is a single
NETObject, it will not be released.
If you want more control over which objects are allowed to escape from a
NETBlock, you can use the
KeepNETObject function. Calling
KeepNETObject on a single object or sequence of objects means they will not be released when the first enclosing
NETBlock ends. If there is an outer enclosing
NETBlock, the objects will be freed when
it ends, however, so if you want the objects to escape a nested set of
NETBlocks, you must call
KeepNETObject at each level. Alternatively, you can call
KeepNETObject[obj, Manual], where the
Manual argument tells
.NET/Link that the object should not be released by any enclosing
NETBlocks. The only way such object will be released is if you manually call
ReleaseNETObject on it.
Keeping .NET objects after a NETBLock ends.
Here is an example that uses
KeepNETObject to allow you to return a list of two objects without releasing them.
BeginNETBlock and
EndNETBlock can be used to provide the same functionality as
NETBlock across more than one evaluation.
EndNETBlock releases all novel .NET objects returned to
Mathematica since the previous matching
BeginNETBlock. These functions are mainly of use during development, when you might want to set a mark in your session, do some work, and then release all novel objects returned to
Mathematica since that point.
BeginNETBlock and
EndNETBlock can be nested. Every
BeginNETBlock should have a matching
EndNETBlock, although it is not a serious error to forget to call
EndNETBlock, even if you have nested levels of them—you will only fail to release some objects.
LoadedNETObjects
LoadedNETObjects[] returns a list of all .NET objects that are currently referenced in
Mathematica. This includes all objects explicitly created with
NETNew and all those that were returned to
Mathematica as the result of a .NET method or property. It does not include objects that have been released with
ReleaseNETObject or through
NETBlock.
LoadedNETObjects is intended mainly for debugging. It is very useful to call it before and after some function you are working on. If the list grows, your function leaks references, and you need to examine its use of
NETBlock and/or
ReleaseNETObject.
Enums
Enumerations in .NET are a special kind of class, with each member of the enumeration represented as a static constant field in the class. Although the values of enumeration constants are integers,
.NET/Link does not convert them into integers when they are returned to
Mathematica. This is because enum values are probably only going to be passed back into some other .NET method—it is not likely that you will want to operate on them as integers in
Mathematica. In this case, it is more meaningful to have them appear in
Mathematica as objects of a class instead of just cryptic integer values.
Suppose you have a
Button object
btn that you want to anchor in a form so that it stays in a fixed position with respect to one or more edges as the form is resized. To do this, you set the button's
Anchor property to a value from the
AnchorStyles enum. First load the
AnchorStyles type, as is always necessary when you want to access a static member of the type.
| Out[16]= |  |
You refer to a member of this enum just like any other static field.
| Out[17]= |  |
The enum is represented in
Mathematica by a strongly typed object reference that is more meaningful to a programmer than the raw integer value, which happens to be 1. We can use the
NETObjectToExpression function to convert the object reference to its integer value.
| Out[18]= |  |
Now apply it to the button.
For any argument that is typed as an enum, you can pass either an instance of the enum class or a raw integer value. This