-
-
Notifications
You must be signed in to change notification settings - Fork 158
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)
-
hxcslibrary (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 to20as described above) - .NET target (by default set to
net, but could be changed using-D net-target=xxx, wherexxxcould bemicro,compactor some other). - .NET std path (set by
-net-stdoption, by default points tonetlibdirectory insidehxcslibrary)
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.
UnityEnginebecomesunityengine(note thatSystemnamespace is also prefixed withcs, soSystem.Corebecomescs.system.core) - inner classes are generated as
OuterClassName_InnerClassNameand placed into theOuterClassNamemodule. So for example for an inner classBinside a classAinside a namespaceSomething, the full haxe type path will besomething.A.A_B. Note however, that if you doimport something.A, bothAandA_Bclass 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 dllcompile to a .NET assembly instead of an executable file. Added automatically when no-mainis specified. -
-D real-positiondon't generate#linedirectives that map C# expression positions to original.hxfiles. Useful for tracking down issues related to code generation. -
-D no-rootgenerate package-less haxe types in thehaxe.rootnamespace to avoid conflicts with other types in the root namespace -
-D erase-genericsfully 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-compilationonly generate C# sources and don't invoke C# compiler on them. -
-D keep-old-outputby 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.
-
@:nativeGenon classes: don't generate reflection, generate proper type parameters. This is useful for some sort of interop, but slows down reflection and structural typing -
@:nativeGenon "flat" enums: generate C# enum, but note that C# enums are not-nullable unlike haxe enums, so usingnullwill be generated as a default enum value (0-indexed constructor). -
@:propertyon non-physical fields (those withget/set/neveraccessors) - generate native C# properties. useful for implementing extern interfaces or providing API for use from C# -
@:eventon variables generate an event delegate (this also requires pairingadd_EventName,remove_EventNamemethods with relevant signatures (TODO: this requires better explanation with an example) -
@:protectedon 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) -
@:structon 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);
}