-
-
Notifications
You must be signed in to change notification settings - Fork 152
Haxe C#
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.
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.
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.
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 to20
as described above) - .NET target (by default set to
net
, but could be changed using-D net-target=xxx
, wherexxx
could bemicro
,compact
or some other). - .NET std path (set by
-net-std
option, by default points tonetlib
directory insidehxcs
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/
.
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
becomesunityengine
(note thatSystem
namespace is also prefixed withcs
, soSystem.Core
becomescs.system.core
) - inner classes are generated as
OuterClassName_InnerClassName
and placed into theOuterClassName
module. So for example for an inner classB
inside a classA
inside a namespaceSomething
, the full haxe type path will besomething.A.A_B
. Note however, that if you doimport something.A
, bothA
andA_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>
becomesDictionary_2<K,V>
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 thehaxe.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
.
-
@: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 usingnull
will be generated as a default enum value (0-indexed constructor). -
@:property
on non-physical fields (those withget/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 pairingadd_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
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);
}
TODO: @:classCode
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);
}