This package provides interoperability between Julia and Common Language Runtime
, the execution engine of .NET
applications. Many languages run on CLR, including C#
, Visual Basic .NET
and PowerShell
.
Julia
version 1.3+.NET Core Runtime
version 2.2+ (Download)
WinForms
and other GUI-related features require a desktop runtime.
If the package fails to locate the runtime, set DOTNET_ROOT
environment variable to the path containing the dotnet
or dotnet.exe
binary.
.NET Framework
is not supported yet.
In the REPL, type ]add DotNET
and press Enter
.
(v1.x) pkg> add DotNET
Or use Pkg.add
for more options:
julia> using Pkg julia> Pkg.add(PackageSpec(url = "https://github.com/azurefx/DotNET.jl"))
julia> using DotNET
DotNET.jl
provides the T"AssemblyQualifiedTypeName"
literal for type reference:
julia> Console = T"System.Console, mscorlib" System.Console
Given a type object, you can access its properties or methods using the dot operator:
julia> Console.WriteLine("Hello from .NET!"); Hello from .NET!
To create an object, use the new
syntax:
julia> T"System.Guid".new("CA761232-ED42-11CE-BACD-00AA0057B223") System.Guid("ca761232-ed42-11ce-bacd-00aa0057b223")
All .NET
objects are represented by CLRObject
s in Julia, including types:
julia> typeof(Console) CLRObject julia> typeof(null) CLRObject
null
is a built-in object that does not refer to a valid .NET
object. When you try to access a member on a null
value, a System.NullReferenceException
is thrown.
Arguments passed to .NET
methods are automatically converted to CLRObject
s, and return values are converted to corresponding Julia types:
julia> T"System.Convert".ToInt64("42") 42
Or you could do some explicit conversions:
julia> s = convert(CLRObject, "❤") System.String("❤") julia> DotNET.unbox(s) "❤"
To pass an argument by reference (out
/ref
in C#
), wrap it into a Ref
object:
julia> result = Ref{Int}() Base.RefValue{Int64}(212700848) julia> T"System.Int64".TryParse("1970", result) true julia> result[] 1970 julia> result = Ref(null) Base.RefValue{CLRObject}(null) julia> T"System.Int64".TryParse("2022", result) true julia> result[] System.Int64(2022)
To copy a multidimensional array from .NET
to Julia, use collect
method:
julia> arr = convert(CLRObject, reshape(1:8, 2, 2, 2)) System.Int64[,,]("System.Int64[,,]") julia> collect(arr) 2×ばつ2×ばつ2 Array{Int64, 3}: [:, :, 1] = 1 3 2 4 [:, :, 2] = 5 7 6 8
CLI Array
elements are stored in row-major order, thus the equivalent definition in C#
is
public static long[,,] Get3DArray() { return new long[2, 2, 2] { {{1, 2}, {3, 4}}, {{5, 6}, {7, 8}} }; }
To index into arrays, use arraystore
and arrayload
methods. Note that CLI Array
s use zero-based indexing.
julia> DotNET.arraystore(arr, (1, 1, 1), 0) null julia> DotNET.arrayload(arr, (1, 1, 1)) == collect(arr)[2, 2, 2] true
If an object implements IEnumerable
interface, you can call GetEnumerator
to iterate over the array:
julia> ch = Channel() do it e = arr.GetEnumerator() while e.MoveNext() put!(it, e.Current) end end Channel{Any}(0) (1 item available) julia> collect(ch) 8-element Vector{Any}: 1 ⋮ 8
Or just use the for-in
loop:
for x in arr println(x) end
If you have a DLL
file, you can load it using reflection:
julia> T"System.Reflection.Assembly".LoadFrom(raw"C:\Users\Azure\Desktop\test.dll") System.Reflection.RuntimeAssembly("test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null")
Now you have access to types defined in the assembly.
Generic types are be expressed in the following ways:
julia> ListT = T"System.Collections.Generic.List`1" System.Collections.Generic.List`1[T] julia> ListInt64 = T"System.Collections.Generic.List`1[System.Int64]" System.Collections.Generic.List`1[System.Int64]
The number 1
after the backtick indicates the type System.Collections.Generic.List<T>
has one type parameter. ListT
has a free type variable, just like Vector{T} where T
in Julia. A type that includes at least one type argument is called a constructed type. ListInt64
is a constructed type.
One can substitute type variables and make a constructed type by calling makegenerictype
method:
julia> DotNET.makegenerictype(ListT, T"System.String") System.Collections.Generic.List`1[System.String]
To invoke a generic method, put type arguments into square brackets:
julia> list = ListT.new[T"System.Int64"]() System.Collections.Generic.List`1[System.Int64]("System.Collections.Generic.List`1[System.Int64]")
To create a delegate from a Julia method, use delegate
method:
julia> list = ListT.new[T"System.Int64"](1:5) System.Collections.Generic.List`1[System.Int64]("System.Collections.Generic.List`1[System.Int64]") julia> list.RemoveAll(delegate(iseven, T"System.Predicate`1[System.Int64]")) 2 julia> collect(list) 3-element Vector{Int64}: 1 3 5