Skip to content
Dan Korostelev edited this page Feb 24, 2016 · 6 revisions

The following content is here temporarily and is intended to be in Haxe Manual (target details chapter: http://haxe.org/manual/target-details.html). As soon as it's ready, it will be moved to the official manual.

Getting started

Haxe can be used as a language for .NET platform through its C# target. Let's make a simple program using .NET Console class:

import cs.system.Console;

class Main {
    static function main() {
        Console.Write("Enter your name: ");
        var name = Console.ReadLine();
        Console.WriteLine('Hello, $name!');
        Console.ReadKey();
    }
}

To compile Haxe to C# we need two obvious prerequisites installed:

  • .NET development framework (either Microsoft.NET or Mono)
  • hxcs library (via haxelib)

After that we can compile to C# using the -cs option from either the command line or an hxml-file:

haxe -main Main -cs out

The compiler will output C# sources into out/src folder, then call C# compiler to build Main.exe file into out/bin folder.

.NET version and external libraries

By default, Haxe uses basic .NET 2.0 API provided by hxcs library (it ships mscorlib.dll and System.dll from the Mono project). We can specify different .NET version by providing -D net-ver=xx define, where xx is major and minor digits of .NET version number, i.e. -D net-ver=40 for setting .NET version to 4.0. Note that currently, hxcs library only ships DLL files for .NET 2.0 and 4.0.

Using custom .NET distribution

We can make Haxe use a custom set of DLL files as standard .NET framework. To do that, we need to first learn about how Haxe finds standard .NET libraries. Haxe/C# looks for .DLL files in a directory path, constructed from three components:

  • .NET version (set by -D net-ver=xx, defaults to 20 as described above)
  • .NET target (by default set to net, but could be changed using -D net-target=xxx, where xxx could be micro, compact or some other).
  • .NET std path (set by -net-std option, by default points to netlib directory inside hxcs library)

The resulting search path will be <net_std_path>/<net_target>-<net_ver>/, taking in the consideration default values described above, without any specific configuration haxe will load all .NET DLL files found in <hxcs_install_path>/netlib/net-20/.

Now if we provide the following options:

-D net-target=micro -D net-ver=35 -net-std=/dotnet

Haxe will load all .NET DLL files found in /dotnet/micro-35/.

Using external libraries

Haxe can directly load .NET assembly files (.DLL) and convert its type definitions for use as Haxe types. To load a .NET assembly, use -net-lib library.dll compiler option. Haxe will then automatically parse types defined in that assembly file and make them available for import as Haxe types.

Some changes are performed to type naming of C# classes to make them fit into Haxe type system, namely:

  • namespaces are lowercased to follow haxe package naming rules, so i.e. UnityEngine becomes unityengine (note that System namespace is also prefixed with cs, so System.Core becomes cs.system.core)
  • inner classes are generated as OuterClassName_InnerClassName and placed into the OuterClassName module. So for example for an inner class B inside a class A inside a namespace Something, the full haxe type path will be something.A.A_B. Note however, that if you do import something.A, both A and A_B class will be available within your module as per standard Haxe import mechanism.
  • classes with type parameters have numbers of type params appended to their name, for example Dictionary<K,V> becomes Dictionary_2<K,V>

Defines

Besides -D net-ver and -D net-target:

  • -D dll compile to a .NET assembly instead of an executable file. Added automatically when no -main is specified.
  • -D real-position don't generate #line directives that map C# expression positions to original .hx files. Useful for tracking down issues related to code generation.
  • -D no-root generate package-less haxe types in the haxe.root namespace to avoid conflicts with other types in the root namespace
  • -D erase-generics fully erase type parameters from generated C# files and generate non-generic classes. This is useful in some cases, like working with .NET Micro Framework or preventing generics-related issues with Unity3D AOT compiler.
  • -D no-compilation only generate C# sources and don't invoke C# compiler on them.
  • -D keep-old-output by default haxe cleans up stale generated source files from the output directory. This define disables that behaviour.
  • -D dll-import (TODO describe this new stuff)

Haxe automatically adds NET_xx defines where xx is major and minor version numbers .NET versions up to selected one. For example, when using .NET 4.0 (by providing -D net-ver=40), we have the following defines set automatically: NET_20, NET_21, NET_30, NET_35 and NET_40. If we had -D net-ver=30, we would only have NET_20, NET_21 and NET_30.

Metadata

  • @:nativeGen on classes: don't generate reflection, generate proper type parameters. This is useful for some sort of interop, but slows down reflection and structural typing
  • @:nativeGen on "flat" enums: generate C# enum, but note that C# enums are not-nullable unlike haxe enums, so using null will be generated as a default enum value (0-indexed constructor).
  • @:property on non-physical fields (those with get/set/never accessors) - generate native C# properties. useful for implementing extern interfaces or providing API for use from C#
  • @:event on variables generate an event delegate (this also requires pairing add_EventName, remove_EventName methods with relevant signatures (TODO: this requires better explanation with an example)
  • @:protected on a field: mark field as protected instead of public (could affect reflection, but useful for hiding fields when providing API for use from outside Haxe)
  • @:struct on a class: generate struct instead of class

Injecting raw C# code

Expression injection

In some cases it may be needed to inject raw C# code into Haxe-generated code. This is possible by using untyped __cs__ call, for example:

public function isBool(v:Dynamic):Bool {
    return untyped __cs__("v is bool");
}

The untyped __cs__ syntax also supports code interpolation which means that you can insert haxe expressions into injected C# code. For example, the example above could have been made inline, but because it always generates "v is bool", it won't work when the given argument is not named "v" in the calling scope. To deal with that, we could rewrite our function using code interpolation, as follows:

public inline function isBool(v:Dynamic):Bool {
    return untyped __cs__("{0} is bool", v);
}

Class code injection

TODO: @:classCode

Function code injection

We can use @:functionCode metadata for a method to generate raw C# code inside a method body. It completely replaces any haxe expressions in method body. For example:

@:functionCode("return (v is int);")
function isInt(v:Dynamic):Bool {
    return false;
}

will generate:

public virtual bool isInt(object v) {
	return (v is int);
}